Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a9b8b7e1e2 | |||
| 966e7ccd8a | |||
| d58be08fee | |||
| eb057b51d3 | |||
| 8c88e7fd5b | |||
| 756d3aa48f | |||
| 1a4dbd9cba | |||
| c68176ad09 | |||
| b0ccf3464f | |||
| dcaff3d1b8 | |||
| 693e0143b5 | |||
| cdc80bdbbd | |||
| 948bca1150 | |||
| 68c4b26031 | |||
| 56bed8206e | |||
| 8e2f06c211 | |||
| 08f2fe84d7 | |||
| 0e5764cf5d | |||
| e6a5751959 | |||
| eefc738ee2 | |||
| 322650345c | |||
| 5c77a44611 | |||
| 28900d0981 | |||
| 77281e11f5 | |||
| e06b0e79cc | |||
| d6f31b8775 | |||
| d862c85874 | |||
| 5bd1b70cd9 | |||
| 191b9b9c1f | |||
| c36cdb1e42 | |||
| 4c53bd3763 | |||
| 7984670a3c | |||
| a58a95f125 | |||
| 378b4cb84b | |||
| ec32f3b681 | |||
| ef5f1a8f8c | |||
| 46f152d5d2 | |||
| f3a096b660 | |||
| c8de4e13f9 | |||
| 1ee3fc79f0 |
2
.gitignore
vendored
@@ -31,3 +31,5 @@ Thumbs.db
|
||||
.gradle
|
||||
build/
|
||||
*.iml
|
||||
|
||||
art/
|
||||
|
||||
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "libs/drag-sort-listview"]
|
||||
path = libs/drag-sort-listview
|
||||
url = https://github.com/iSoron/drag-sort-listview.git
|
||||
@@ -1,7 +1,6 @@
|
||||
# Habits Tracker
|
||||
|
||||
Habits Tracker is a simple Android app that helps you create and maintain good habits. Detailed graphs and statistics show you how your habits improved over time. It is completely ad-free and open source, with no intrusive permissions. Join the open beta at [Google Play Store](https://play.google.com/apps/testing/org.isoron.uhabits).
|
||||
# Loop Habit Tracker
|
||||
|
||||
Loop is a simple Android app that helps you create and maintain good habits. Detailed graphs and statistics show you how your habits improved over time. It is completely ad-free and open source, with no intrusive permissions. Join the open beta at [Google Play Store](https://play.google.com/apps/testing/org.isoron.uhabits).
|
||||
|
||||
## Features
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ android {
|
||||
dependencies {
|
||||
compile 'com.android.support:support-v4:23.1.1'
|
||||
compile 'com.github.paolorotolo:appintro:3.4.0'
|
||||
compile project(':libs:drag-sort-listview:library')
|
||||
compile files('libs/ActiveAndroid.jar')
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<manifest
|
||||
package="org.isoron.uhabits"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="4"
|
||||
android:versionName="1.0.0">
|
||||
android:versionCode="6"
|
||||
android:versionName="1.1.0">
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.VIBRATE"/>
|
||||
@@ -11,7 +11,7 @@
|
||||
<application
|
||||
android:name="com.activeandroid.app.Application"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:label="@string/main_activity_title"
|
||||
android:theme="@style/AppBaseTheme"
|
||||
android:backupAgent=".HabitsBackupAgent">
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
<meta-data
|
||||
android:name="AA_DB_VERSION"
|
||||
android:value="9"/>
|
||||
android:value="11"/>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.backup.api_key"
|
||||
@@ -29,8 +29,9 @@
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name">
|
||||
<intent-filter>
|
||||
android:label="@string/main_activity_title">
|
||||
<intent-filter
|
||||
android:label="@string/app_name">
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
@@ -50,7 +51,7 @@
|
||||
|
||||
<activity
|
||||
android:name=".SettingsActivity"
|
||||
android:label="Settings"
|
||||
android:label="@string/settings"
|
||||
android:parentActivityName=".MainActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
|
||||
3
app/src/main/assets/migrations/10.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
delete from Score;
|
||||
delete from Streak;
|
||||
delete from Checkmarks;
|
||||
1
app/src/main/assets/migrations/11.sql
Normal file
@@ -0,0 +1 @@
|
||||
alter table habits add column reminder_days integer not null default 127;
|
||||
@@ -1,471 +0,0 @@
|
||||
package com.mobeta.android.dslv;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.AdapterView;
|
||||
|
||||
/**
|
||||
* Class that starts and stops item drags on a {@link DragSortListView}
|
||||
* based on touch gestures. This class also inherits from
|
||||
* {@link SimpleFloatViewManager}, which provides basic float View
|
||||
* creation.
|
||||
*
|
||||
* An instance of this class is meant to be passed to the methods
|
||||
* {@link DragSortListView#setTouchListener()} and
|
||||
* {@link DragSortListView#setFloatViewManager()} of your
|
||||
* {@link DragSortListView} instance.
|
||||
*/
|
||||
public class DragSortController extends SimpleFloatViewManager implements View.OnTouchListener, GestureDetector.OnGestureListener {
|
||||
|
||||
/**
|
||||
* Drag init mode enum.
|
||||
*/
|
||||
public static final int ON_DOWN = 0;
|
||||
public static final int ON_DRAG = 1;
|
||||
public static final int ON_LONG_PRESS = 2;
|
||||
|
||||
private int mDragInitMode = ON_DOWN;
|
||||
|
||||
private boolean mSortEnabled = true;
|
||||
|
||||
/**
|
||||
* Remove mode enum.
|
||||
*/
|
||||
public static final int CLICK_REMOVE = 0;
|
||||
public static final int FLING_REMOVE = 1;
|
||||
|
||||
/**
|
||||
* The current remove mode.
|
||||
*/
|
||||
private int mRemoveMode;
|
||||
|
||||
private boolean mRemoveEnabled = false;
|
||||
private boolean mIsRemoving = false;
|
||||
|
||||
private GestureDetector mDetector;
|
||||
|
||||
private GestureDetector mFlingRemoveDetector;
|
||||
|
||||
private int mTouchSlop;
|
||||
|
||||
public static final int MISS = -1;
|
||||
|
||||
private int mHitPos = MISS;
|
||||
private int mFlingHitPos = MISS;
|
||||
|
||||
private int mClickRemoveHitPos = MISS;
|
||||
|
||||
private int[] mTempLoc = new int[2];
|
||||
|
||||
private int mItemX;
|
||||
private int mItemY;
|
||||
|
||||
private int mCurrX;
|
||||
private int mCurrY;
|
||||
|
||||
private boolean mDragging = false;
|
||||
|
||||
private float mFlingSpeed = 500f;
|
||||
|
||||
private int mDragHandleId;
|
||||
|
||||
private int mClickRemoveId;
|
||||
|
||||
private int mFlingHandleId;
|
||||
private boolean mCanDrag;
|
||||
|
||||
private DragSortListView mDslv;
|
||||
private int mPositionX;
|
||||
|
||||
/**
|
||||
* Calls {@link #DragSortController(DragSortListView, int)} with a
|
||||
* 0 drag handle id, FLING_RIGHT_REMOVE remove mode,
|
||||
* and ON_DOWN drag init. By default, sorting is enabled, and
|
||||
* removal is disabled.
|
||||
*
|
||||
* @param dslv The DSLV instance
|
||||
*/
|
||||
public DragSortController(DragSortListView dslv) {
|
||||
this(dslv, 0, ON_DOWN, FLING_REMOVE);
|
||||
}
|
||||
|
||||
public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode) {
|
||||
this(dslv, dragHandleId, dragInitMode, removeMode, 0);
|
||||
}
|
||||
|
||||
public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode, int removeMode, int clickRemoveId) {
|
||||
this(dslv, dragHandleId, dragInitMode, removeMode, clickRemoveId, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* By default, sorting is enabled, and removal is disabled.
|
||||
*
|
||||
* @param dslv The DSLV instance
|
||||
* @param dragHandleId The resource id of the View that represents
|
||||
* the drag handle in a list item.
|
||||
*/
|
||||
public DragSortController(DragSortListView dslv, int dragHandleId, int dragInitMode,
|
||||
int removeMode, int clickRemoveId, int flingHandleId) {
|
||||
super(dslv);
|
||||
mDslv = dslv;
|
||||
mDetector = new GestureDetector(dslv.getContext(), this);
|
||||
mFlingRemoveDetector = new GestureDetector(dslv.getContext(), mFlingRemoveListener);
|
||||
mFlingRemoveDetector.setIsLongpressEnabled(false);
|
||||
mTouchSlop = ViewConfiguration.get(dslv.getContext()).getScaledTouchSlop();
|
||||
mDragHandleId = dragHandleId;
|
||||
mClickRemoveId = clickRemoveId;
|
||||
mFlingHandleId = flingHandleId;
|
||||
setRemoveMode(removeMode);
|
||||
setDragInitMode(dragInitMode);
|
||||
}
|
||||
|
||||
|
||||
public int getDragInitMode() {
|
||||
return mDragInitMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set how a drag is initiated. Needs to be one of
|
||||
* {@link ON_DOWN}, {@link ON_DRAG}, or {@link ON_LONG_PRESS}.
|
||||
*
|
||||
* @param mode The drag init mode.
|
||||
*/
|
||||
public void setDragInitMode(int mode) {
|
||||
mDragInitMode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable list item sorting. Disabling is useful if only item
|
||||
* removal is desired. Prevents drags in the vertical direction.
|
||||
*
|
||||
* @param enabled Set <code>true</code> to enable list
|
||||
* item sorting.
|
||||
*/
|
||||
public void setSortEnabled(boolean enabled) {
|
||||
mSortEnabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isSortEnabled() {
|
||||
return mSortEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* One of {@link CLICK_REMOVE}, {@link FLING_RIGHT_REMOVE},
|
||||
* {@link FLING_LEFT_REMOVE},
|
||||
* {@link SLIDE_RIGHT_REMOVE}, or {@link SLIDE_LEFT_REMOVE}.
|
||||
*/
|
||||
public void setRemoveMode(int mode) {
|
||||
mRemoveMode = mode;
|
||||
}
|
||||
|
||||
public int getRemoveMode() {
|
||||
return mRemoveMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable item removal without affecting remove mode.
|
||||
*/
|
||||
public void setRemoveEnabled(boolean enabled) {
|
||||
mRemoveEnabled = enabled;
|
||||
}
|
||||
|
||||
public boolean isRemoveEnabled() {
|
||||
return mRemoveEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource id for the View that represents the drag
|
||||
* handle in a list item.
|
||||
*
|
||||
* @param id An android resource id.
|
||||
*/
|
||||
public void setDragHandleId(int id) {
|
||||
mDragHandleId = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource id for the View that represents the fling
|
||||
* handle in a list item.
|
||||
*
|
||||
* @param id An android resource id.
|
||||
*/
|
||||
public void setFlingHandleId(int id) {
|
||||
mFlingHandleId = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource id for the View that represents click
|
||||
* removal button.
|
||||
*
|
||||
* @param id An android resource id.
|
||||
*/
|
||||
public void setClickRemoveId(int id) {
|
||||
mClickRemoveId = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flags to restrict certain motions of the floating View
|
||||
* based on DragSortController settings (such as remove mode).
|
||||
* Starts the drag on the DragSortListView.
|
||||
*
|
||||
* @param position The list item position (includes headers).
|
||||
* @param deltaX Touch x-coord minus left edge of floating View.
|
||||
* @param deltaY Touch y-coord minus top edge of floating View.
|
||||
*
|
||||
* @return True if drag started, false otherwise.
|
||||
*/
|
||||
public boolean startDrag(int position, int deltaX, int deltaY) {
|
||||
|
||||
int dragFlags = 0;
|
||||
if (mSortEnabled && !mIsRemoving) {
|
||||
dragFlags |= DragSortListView.DRAG_POS_Y | DragSortListView.DRAG_NEG_Y;
|
||||
}
|
||||
if (mRemoveEnabled && mIsRemoving) {
|
||||
dragFlags |= DragSortListView.DRAG_POS_X;
|
||||
dragFlags |= DragSortListView.DRAG_NEG_X;
|
||||
}
|
||||
|
||||
mDragging = mDslv.startDrag(position - mDslv.getHeaderViewsCount(), dragFlags, deltaX,
|
||||
deltaY);
|
||||
return mDragging;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent ev) {
|
||||
if (!mDslv.isDragEnabled() || mDslv.listViewIntercepted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mDetector.onTouchEvent(ev);
|
||||
if (mRemoveEnabled && mDragging && mRemoveMode == FLING_REMOVE) {
|
||||
mFlingRemoveDetector.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
int action = ev.getAction() & MotionEvent.ACTION_MASK;
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mCurrX = (int) ev.getX();
|
||||
mCurrY = (int) ev.getY();
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (mRemoveEnabled && mIsRemoving) {
|
||||
int x = mPositionX >= 0 ? mPositionX : -mPositionX;
|
||||
int removePoint = mDslv.getWidth() / 2;
|
||||
if (x > removePoint) {
|
||||
mDslv.stopDragWithVelocity(true, 0);
|
||||
}
|
||||
}
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mIsRemoving = false;
|
||||
mDragging = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides to provide fading when slide removal is enabled.
|
||||
*/
|
||||
@Override
|
||||
public void onDragFloatView(View floatView, Point position, Point touch) {
|
||||
|
||||
if (mRemoveEnabled && mIsRemoving) {
|
||||
mPositionX = position.x;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position to start dragging based on the ACTION_DOWN
|
||||
* MotionEvent. This function simply calls
|
||||
* {@link #dragHandleHitPosition(MotionEvent)}. Override
|
||||
* to change drag handle behavior;
|
||||
* this function is called internally when an ACTION_DOWN
|
||||
* event is detected.
|
||||
*
|
||||
* @param ev The ACTION_DOWN MotionEvent.
|
||||
*
|
||||
* @return The list position to drag if a drag-init gesture is
|
||||
* detected; MISS if unsuccessful.
|
||||
*/
|
||||
public int startDragPosition(MotionEvent ev) {
|
||||
return dragHandleHitPosition(ev);
|
||||
}
|
||||
|
||||
public int startFlingPosition(MotionEvent ev) {
|
||||
return mRemoveMode == FLING_REMOVE ? flingHandleHitPosition(ev) : MISS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for the touch of an item's drag handle (specified by
|
||||
* {@link #setDragHandleId(int)}), and returns that item's position
|
||||
* if a drag handle touch was detected.
|
||||
*
|
||||
* @param ev The ACTION_DOWN MotionEvent.
|
||||
|
||||
* @return The list position of the item whose drag handle was
|
||||
* touched; MISS if unsuccessful.
|
||||
*/
|
||||
public int dragHandleHitPosition(MotionEvent ev) {
|
||||
return viewIdHitPosition(ev, mDragHandleId);
|
||||
}
|
||||
|
||||
public int flingHandleHitPosition(MotionEvent ev) {
|
||||
return viewIdHitPosition(ev, mFlingHandleId);
|
||||
}
|
||||
|
||||
public int viewIdHitPosition(MotionEvent ev, int id) {
|
||||
final int x = (int) ev.getX();
|
||||
final int y = (int) ev.getY();
|
||||
|
||||
int touchPos = mDslv.pointToPosition(x, y); // includes headers/footers
|
||||
|
||||
final int numHeaders = mDslv.getHeaderViewsCount();
|
||||
final int numFooters = mDslv.getFooterViewsCount();
|
||||
final int count = mDslv.getCount();
|
||||
|
||||
// Log.d("mobeta", "touch down on position " + itemnum);
|
||||
// We're only interested if the touch was on an
|
||||
// item that's not a header or footer.
|
||||
if (touchPos != AdapterView.INVALID_POSITION && touchPos >= numHeaders
|
||||
&& touchPos < (count - numFooters)) {
|
||||
final View item = mDslv.getChildAt(touchPos - mDslv.getFirstVisiblePosition());
|
||||
final int rawX = (int) ev.getRawX();
|
||||
final int rawY = (int) ev.getRawY();
|
||||
|
||||
View dragBox = id == 0 ? item : (View) item.findViewById(id);
|
||||
if (dragBox != null) {
|
||||
dragBox.getLocationOnScreen(mTempLoc);
|
||||
|
||||
if (rawX > mTempLoc[0] && rawY > mTempLoc[1] &&
|
||||
rawX < mTempLoc[0] + dragBox.getWidth() &&
|
||||
rawY < mTempLoc[1] + dragBox.getHeight()) {
|
||||
|
||||
mItemX = item.getLeft();
|
||||
mItemY = item.getTop();
|
||||
|
||||
return touchPos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MISS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent ev) {
|
||||
if (mRemoveEnabled && mRemoveMode == CLICK_REMOVE) {
|
||||
mClickRemoveHitPos = viewIdHitPosition(ev, mClickRemoveId);
|
||||
}
|
||||
|
||||
mHitPos = startDragPosition(ev);
|
||||
if (mHitPos != MISS && mDragInitMode == ON_DOWN) {
|
||||
startDrag(mHitPos, (int) ev.getX() - mItemX, (int) ev.getY() - mItemY);
|
||||
}
|
||||
|
||||
mIsRemoving = false;
|
||||
mCanDrag = true;
|
||||
mPositionX = 0;
|
||||
mFlingHitPos = startFlingPosition(ev);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
|
||||
if(e1 == null) return false;
|
||||
if(e2 == null) return false;
|
||||
|
||||
final int x1 = (int) e1.getX();
|
||||
final int y1 = (int) e1.getY();
|
||||
final int x2 = (int) e2.getX();
|
||||
final int y2 = (int) e2.getY();
|
||||
final int deltaX = x2 - mItemX;
|
||||
final int deltaY = y2 - mItemY;
|
||||
|
||||
if (mCanDrag && !mDragging && (mHitPos != MISS || mFlingHitPos != MISS)) {
|
||||
if (mHitPos != MISS) {
|
||||
if (mDragInitMode == ON_DRAG && Math.abs(y2 - y1) > mTouchSlop && mSortEnabled) {
|
||||
startDrag(mHitPos, deltaX, deltaY);
|
||||
}
|
||||
else if (mDragInitMode != ON_DOWN && Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled)
|
||||
{
|
||||
mIsRemoving = true;
|
||||
startDrag(mFlingHitPos, deltaX, deltaY);
|
||||
}
|
||||
} else if (mFlingHitPos != MISS) {
|
||||
if (Math.abs(x2 - x1) > mTouchSlop && mRemoveEnabled) {
|
||||
mIsRemoving = true;
|
||||
startDrag(mFlingHitPos, deltaX, deltaY);
|
||||
} else if (Math.abs(y2 - y1) > mTouchSlop) {
|
||||
mCanDrag = false; // if started to scroll the list then
|
||||
// don't allow sorting nor fling-removing
|
||||
}
|
||||
}
|
||||
}
|
||||
// return whatever
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
// Log.d("mobeta", "lift listener long pressed");
|
||||
if (mHitPos != MISS && mDragInitMode == ON_LONG_PRESS) {
|
||||
mDslv.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
|
||||
startDrag(mHitPos, mCurrX - mItemX, mCurrY - mItemY);
|
||||
}
|
||||
}
|
||||
|
||||
// complete the OnGestureListener interface
|
||||
@Override
|
||||
public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// complete the OnGestureListener interface
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent ev) {
|
||||
if (mRemoveEnabled && mRemoveMode == CLICK_REMOVE) {
|
||||
if (mClickRemoveHitPos != MISS) {
|
||||
mDslv.removeItem(mClickRemoveHitPos - mDslv.getHeaderViewsCount());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// complete the OnGestureListener interface
|
||||
@Override
|
||||
public void onShowPress(MotionEvent ev) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
private GestureDetector.OnGestureListener mFlingRemoveListener =
|
||||
new GestureDetector.SimpleOnGestureListener() {
|
||||
@Override
|
||||
public final boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
|
||||
float velocityY) {
|
||||
// Log.d("mobeta", "on fling remove called");
|
||||
if (mRemoveEnabled && mIsRemoving) {
|
||||
int w = mDslv.getWidth();
|
||||
int minPos = w / 5;
|
||||
if (velocityX > mFlingSpeed) {
|
||||
if (mPositionX > -minPos) {
|
||||
mDslv.stopDragWithVelocity(true, velocityX);
|
||||
}
|
||||
} else if (velocityX < -mFlingSpeed) {
|
||||
if (mPositionX < minPos) {
|
||||
mDslv.stopDragWithVelocity(true, velocityX);
|
||||
}
|
||||
}
|
||||
mIsRemoving = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,241 +0,0 @@
|
||||
package com.mobeta.android.dslv;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
|
||||
|
||||
/**
|
||||
* A subclass of {@link android.widget.CursorAdapter} that provides
|
||||
* reordering of the elements in the Cursor based on completed
|
||||
* drag-sort operations. The reordering is a simple mapping of
|
||||
* list positions into Cursor positions (the Cursor is unchanged).
|
||||
* To persist changes made by drag-sorts, one can retrieve the
|
||||
* mapping with the {@link #getCursorPositions()} method, which
|
||||
* returns the reordered list of Cursor positions.
|
||||
*
|
||||
* An instance of this class is passed
|
||||
* to {@link DragSortListView#setAdapter(ListAdapter)} and, since
|
||||
* this class implements the {@link DragSortListView.DragSortListener}
|
||||
* interface, it is automatically set as the DragSortListener for
|
||||
* the DragSortListView instance.
|
||||
*/
|
||||
public abstract class DragSortCursorAdapter extends CursorAdapter implements DragSortListView.DragSortListener {
|
||||
|
||||
public static final int REMOVED = -1;
|
||||
|
||||
/**
|
||||
* Key is ListView position, value is Cursor position
|
||||
*/
|
||||
private SparseIntArray mListMapping = new SparseIntArray();
|
||||
|
||||
private ArrayList<Integer> mRemovedCursorPositions = new ArrayList<Integer>();
|
||||
|
||||
public DragSortCursorAdapter(Context context, Cursor c) {
|
||||
super(context, c);
|
||||
}
|
||||
|
||||
public DragSortCursorAdapter(Context context, Cursor c, boolean autoRequery) {
|
||||
super(context, c, autoRequery);
|
||||
}
|
||||
|
||||
public DragSortCursorAdapter(Context context, Cursor c, int flags) {
|
||||
super(context, c, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps Cursor and clears list-Cursor mapping.
|
||||
*
|
||||
* @see android.widget.CursorAdapter#swapCursor(android.database.Cursor)
|
||||
*/
|
||||
@Override
|
||||
public Cursor swapCursor(Cursor newCursor) {
|
||||
Cursor old = super.swapCursor(newCursor);
|
||||
resetMappings();
|
||||
return old;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes Cursor and clears list-Cursor mapping.
|
||||
*
|
||||
* @see android.widget.CursorAdapter#changeCursor(android.database.Cursor)
|
||||
*/
|
||||
@Override
|
||||
public void changeCursor(Cursor cursor) {
|
||||
super.changeCursor(cursor);
|
||||
resetMappings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets list-cursor mapping.
|
||||
*/
|
||||
public void reset() {
|
||||
resetMappings();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void resetMappings() {
|
||||
mListMapping.clear();
|
||||
mRemovedCursorPositions.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return super.getItem(mListMapping.get(position, position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return super.getItemId(mListMapping.get(position, position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getDropDownView(int position, View convertView, ViewGroup parent) {
|
||||
return super.getDropDownView(mListMapping.get(position, position), convertView, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
return super.getView(mListMapping.get(position, position), convertView, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* On drop, this updates the mapping between Cursor positions
|
||||
* and ListView positions. The Cursor is unchanged. Retrieve
|
||||
* the current mapping with {@link getCursorPositions()}.
|
||||
*
|
||||
* @see DragSortListView.DropListener#drop(int, int)
|
||||
*/
|
||||
@Override
|
||||
public void drop(int from, int to) {
|
||||
if (from != to) {
|
||||
int cursorFrom = mListMapping.get(from, from);
|
||||
|
||||
if (from > to) {
|
||||
for (int i = from; i > to; --i) {
|
||||
mListMapping.put(i, mListMapping.get(i - 1, i - 1));
|
||||
}
|
||||
} else {
|
||||
for (int i = from; i < to; ++i) {
|
||||
mListMapping.put(i, mListMapping.get(i + 1, i + 1));
|
||||
}
|
||||
}
|
||||
mListMapping.put(to, cursorFrom);
|
||||
|
||||
cleanMapping();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On remove, this updates the mapping between Cursor positions
|
||||
* and ListView positions. The Cursor is unchanged. Retrieve
|
||||
* the current mapping with {@link getCursorPositions()}.
|
||||
*
|
||||
* @see DragSortListView.RemoveListener#remove(int)
|
||||
*/
|
||||
@Override
|
||||
public void remove(int which) {
|
||||
int cursorPos = mListMapping.get(which, which);
|
||||
if (!mRemovedCursorPositions.contains(cursorPos)) {
|
||||
mRemovedCursorPositions.add(cursorPos);
|
||||
}
|
||||
|
||||
int newCount = getCount();
|
||||
for (int i = which; i < newCount; ++i) {
|
||||
mListMapping.put(i, mListMapping.get(i + 1, i + 1));
|
||||
}
|
||||
|
||||
mListMapping.delete(newCount);
|
||||
|
||||
cleanMapping();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. Just completes DragSortListener interface.
|
||||
*/
|
||||
@Override
|
||||
public void drag(int from, int to) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecessary mappings from sparse array.
|
||||
*/
|
||||
private void cleanMapping() {
|
||||
ArrayList<Integer> toRemove = new ArrayList<Integer>();
|
||||
|
||||
int size = mListMapping.size();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (mListMapping.keyAt(i) == mListMapping.valueAt(i)) {
|
||||
toRemove.add(mListMapping.keyAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
size = toRemove.size();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
mListMapping.delete(toRemove.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return super.getCount() - mRemovedCursorPositions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Cursor position mapped to by the provided list position
|
||||
* (given all previously handled drag-sort
|
||||
* operations).
|
||||
*
|
||||
* @param position List position
|
||||
*
|
||||
* @return The mapped-to Cursor position
|
||||
*/
|
||||
public int getCursorPosition(int position) {
|
||||
return mListMapping.get(position, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current order of Cursor positions presented by the
|
||||
* list.
|
||||
*/
|
||||
public ArrayList<Integer> getCursorPositions() {
|
||||
ArrayList<Integer> result = new ArrayList<Integer>();
|
||||
|
||||
for (int i = 0; i < getCount(); ++i) {
|
||||
result.add(mListMapping.get(i, i));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list position mapped to by the provided Cursor position.
|
||||
* If the provided Cursor position has been removed by a drag-sort,
|
||||
* this returns {@link #REMOVED}.
|
||||
*
|
||||
* @param cursorPosition A Cursor position
|
||||
* @return The mapped-to list position or REMOVED
|
||||
*/
|
||||
public int getListPosition(int cursorPosition) {
|
||||
if (mRemovedCursorPositions.contains(cursorPosition)) {
|
||||
return REMOVED;
|
||||
}
|
||||
|
||||
int index = mListMapping.indexOfValue(cursorPosition);
|
||||
if (index < 0) {
|
||||
return cursorPosition;
|
||||
} else {
|
||||
return mListMapping.keyAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
package com.mobeta.android.dslv;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Lightweight ViewGroup that wraps list items obtained from user's
|
||||
* ListAdapter. ItemView expects a single child that has a definite
|
||||
* height (i.e. the child's layout height is not MATCH_PARENT).
|
||||
* The width of
|
||||
* ItemView will always match the width of its child (that is,
|
||||
* the width MeasureSpec given to ItemView is passed directly
|
||||
* to the child, and the ItemView measured width is set to the
|
||||
* child's measured width). The height of ItemView can be anything;
|
||||
* the
|
||||
*
|
||||
*
|
||||
* The purpose of this class is to optimize slide
|
||||
* shuffle animations.
|
||||
*/
|
||||
public class DragSortItemView extends ViewGroup {
|
||||
|
||||
private int mGravity = Gravity.TOP;
|
||||
|
||||
public DragSortItemView(Context context) {
|
||||
super(context);
|
||||
|
||||
// always init with standard ListView layout params
|
||||
setLayoutParams(new AbsListView.LayoutParams(
|
||||
ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
//setClipChildren(true);
|
||||
}
|
||||
|
||||
public void setGravity(int gravity) {
|
||||
mGravity = gravity;
|
||||
}
|
||||
|
||||
public int getGravity() {
|
||||
return mGravity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
final View child = getChildAt(0);
|
||||
|
||||
if (child == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mGravity == Gravity.TOP) {
|
||||
child.layout(0, 0, getMeasuredWidth(), child.getMeasuredHeight());
|
||||
} else {
|
||||
child.layout(0, getMeasuredHeight() - child.getMeasuredHeight(), getMeasuredWidth(), getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
|
||||
final View child = getChildAt(0);
|
||||
if (child == null) {
|
||||
setMeasuredDimension(0, width);
|
||||
return;
|
||||
}
|
||||
|
||||
if (child.isLayoutRequested()) {
|
||||
// Always let child be as tall as it wants.
|
||||
measureChild(child, widthMeasureSpec,
|
||||
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
|
||||
}
|
||||
|
||||
if (heightMode == MeasureSpec.UNSPECIFIED) {
|
||||
ViewGroup.LayoutParams lp = getLayoutParams();
|
||||
|
||||
if (lp.height > 0) {
|
||||
height = lp.height;
|
||||
} else {
|
||||
height = child.getMeasuredHeight();
|
||||
}
|
||||
}
|
||||
|
||||
setMeasuredDimension(width, height);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.mobeta.android.dslv;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AbsListView;
|
||||
import android.widget.Checkable;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Lightweight ViewGroup that wraps list items obtained from user's
|
||||
* ListAdapter. ItemView expects a single child that has a definite
|
||||
* height (i.e. the child's layout height is not MATCH_PARENT).
|
||||
* The width of
|
||||
* ItemView will always match the width of its child (that is,
|
||||
* the width MeasureSpec given to ItemView is passed directly
|
||||
* to the child, and the ItemView measured width is set to the
|
||||
* child's measured width). The height of ItemView can be anything;
|
||||
* the
|
||||
*
|
||||
*
|
||||
* The purpose of this class is to optimize slide
|
||||
* shuffle animations.
|
||||
*/
|
||||
public class DragSortItemViewCheckable extends DragSortItemView implements Checkable {
|
||||
|
||||
public DragSortItemViewCheckable(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
View child = getChildAt(0);
|
||||
if (child instanceof Checkable)
|
||||
return ((Checkable) child).isChecked();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(boolean checked) {
|
||||
View child = getChildAt(0);
|
||||
if (child instanceof Checkable)
|
||||
((Checkable) child).setChecked(checked);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toggle() {
|
||||
View child = getChildAt(0);
|
||||
if (child instanceof Checkable)
|
||||
((Checkable) child).toggle();
|
||||
}
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.mobeta.android.dslv;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
// taken from v4 rev. 10 ResourceCursorAdapter.java
|
||||
|
||||
/**
|
||||
* Static library support version of the framework's {@link android.widget.ResourceCursorAdapter}.
|
||||
* Used to write apps that run on platforms prior to Android 3.0. When running
|
||||
* on Android 3.0 or above, this implementation is still used; it does not try
|
||||
* to switch to the framework's implementation. See the framework SDK
|
||||
* documentation for a class overview.
|
||||
*/
|
||||
public abstract class ResourceDragSortCursorAdapter extends DragSortCursorAdapter {
|
||||
private int mLayout;
|
||||
|
||||
private int mDropDownLayout;
|
||||
|
||||
private LayoutInflater mInflater;
|
||||
|
||||
/**
|
||||
* Constructor the enables auto-requery.
|
||||
*
|
||||
* @deprecated This option is discouraged, as it results in Cursor queries
|
||||
* being performed on the application's UI thread and thus can cause poor
|
||||
* responsiveness or even Application Not Responding errors. As an alternative,
|
||||
* use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
|
||||
*
|
||||
* @param context The context where the ListView associated with this adapter is running
|
||||
* @param layout resource identifier of a layout file that defines the views
|
||||
* for this list item. Unless you override them later, this will
|
||||
* define both the item views and the drop down views.
|
||||
*/
|
||||
@Deprecated
|
||||
public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c) {
|
||||
super(context, c);
|
||||
mLayout = mDropDownLayout = layout;
|
||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with default behavior as per
|
||||
* {@link CursorAdapter#CursorAdapter(Context, Cursor, boolean)}; it is recommended
|
||||
* you not use this, but instead {@link #ResourceCursorAdapter(Context, int, Cursor, int)}.
|
||||
* When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
|
||||
* will always be set.
|
||||
*
|
||||
* @param context The context where the ListView associated with this adapter is running
|
||||
* @param layout resource identifier of a layout file that defines the views
|
||||
* for this list item. Unless you override them later, this will
|
||||
* define both the item views and the drop down views.
|
||||
* @param c The cursor from which to get the data.
|
||||
* @param autoRequery If true the adapter will call requery() on the
|
||||
* cursor whenever it changes so the most recent
|
||||
* data is always displayed. Using true here is discouraged.
|
||||
*/
|
||||
public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c, boolean autoRequery) {
|
||||
super(context, c, autoRequery);
|
||||
mLayout = mDropDownLayout = layout;
|
||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard constructor.
|
||||
*
|
||||
* @param context The context where the ListView associated with this adapter is running
|
||||
* @param layout Resource identifier of a layout file that defines the views
|
||||
* for this list item. Unless you override them later, this will
|
||||
* define both the item views and the drop down views.
|
||||
* @param c The cursor from which to get the data.
|
||||
* @param flags Flags used to determine the behavior of the adapter,
|
||||
* as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
|
||||
*/
|
||||
public ResourceDragSortCursorAdapter(Context context, int layout, Cursor c, int flags) {
|
||||
super(context, c, flags);
|
||||
mLayout = mDropDownLayout = layout;
|
||||
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflates view(s) from the specified XML file.
|
||||
*
|
||||
* @see android.widget.CursorAdapter#newView(android.content.Context,
|
||||
* android.database.Cursor, ViewGroup)
|
||||
*/
|
||||
@Override
|
||||
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
return mInflater.inflate(mLayout, parent, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
return mInflater.inflate(mDropDownLayout, parent, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the layout resource of the item views.</p>
|
||||
*
|
||||
* @param layout the layout resources used to create item views
|
||||
*/
|
||||
public void setViewResource(int layout) {
|
||||
mLayout = layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Sets the layout resource of the drop down views.</p>
|
||||
*
|
||||
* @param dropDownLayout the layout resources used to create drop down views
|
||||
*/
|
||||
public void setDropDownViewResource(int dropDownLayout) {
|
||||
mDropDownLayout = dropDownLayout;
|
||||
}
|
||||
}
|
||||
@@ -1,422 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.mobeta.android.dslv;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ImageView;
|
||||
|
||||
// taken from sdk/sources/android-16/android/widget/SimpleCursorAdapter.java
|
||||
|
||||
/**
|
||||
* An easy adapter to map columns from a cursor to TextViews or ImageViews
|
||||
* defined in an XML file. You can specify which columns you want, which
|
||||
* views you want to display the columns, and the XML file that defines
|
||||
* the appearance of these views.
|
||||
*
|
||||
* Binding occurs in two phases. First, if a
|
||||
* {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
|
||||
* {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
|
||||
* is invoked. If the returned value is true, binding has occured. If the
|
||||
* returned value is false and the view to bind is a TextView,
|
||||
* {@link #setViewText(TextView, String)} is invoked. If the returned value
|
||||
* is false and the view to bind is an ImageView,
|
||||
* {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
|
||||
* binding can be found, an {@link IllegalStateException} is thrown.
|
||||
*
|
||||
* If this adapter is used with filtering, for instance in an
|
||||
* {@link android.widget.AutoCompleteTextView}, you can use the
|
||||
* {@link android.widget.SimpleCursorAdapter.CursorToStringConverter} and the
|
||||
* {@link android.widget.FilterQueryProvider} interfaces
|
||||
* to get control over the filtering process. You can refer to
|
||||
* {@link #convertToString(android.database.Cursor)} and
|
||||
* {@link #runQueryOnBackgroundThread(CharSequence)} for more information.
|
||||
*/
|
||||
public class SimpleDragSortCursorAdapter extends ResourceDragSortCursorAdapter {
|
||||
/**
|
||||
* A list of columns containing the data to bind to the UI.
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
protected int[] mFrom;
|
||||
/**
|
||||
* A list of View ids representing the views to which the data must be bound.
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
protected int[] mTo;
|
||||
|
||||
private int mStringConversionColumn = -1;
|
||||
private CursorToStringConverter mCursorToStringConverter;
|
||||
private ViewBinder mViewBinder;
|
||||
|
||||
String[] mOriginalFrom;
|
||||
|
||||
/**
|
||||
* Constructor the enables auto-requery.
|
||||
*
|
||||
* @deprecated This option is discouraged, as it results in Cursor queries
|
||||
* being performed on the application's UI thread and thus can cause poor
|
||||
* responsiveness or even Application Not Responding errors. As an alternative,
|
||||
* use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
|
||||
*/
|
||||
@Deprecated
|
||||
public SimpleDragSortCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
|
||||
super(context, layout, c);
|
||||
mTo = to;
|
||||
mOriginalFrom = from;
|
||||
findColumns(c, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard constructor.
|
||||
*
|
||||
* @param context The context where the ListView associated with this
|
||||
* SimpleListItemFactory is running
|
||||
* @param layout resource identifier of a layout file that defines the views
|
||||
* for this list item. The layout file should include at least
|
||||
* those named views defined in "to"
|
||||
* @param c The database cursor. Can be null if the cursor is not available yet.
|
||||
* @param from A list of column names representing the data to bind to the UI. Can be null
|
||||
* if the cursor is not available yet.
|
||||
* @param to The views that should display column in the "from" parameter.
|
||||
* These should all be TextViews. The first N views in this list
|
||||
* are given the values of the first N columns in the from
|
||||
* parameter. Can be null if the cursor is not available yet.
|
||||
* @param flags Flags used to determine the behavior of the adapter,
|
||||
* as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
|
||||
*/
|
||||
public SimpleDragSortCursorAdapter(Context context, int layout,
|
||||
Cursor c, String[] from, int[] to, int flags) {
|
||||
super(context, layout, c, flags);
|
||||
mTo = to;
|
||||
mOriginalFrom = from;
|
||||
findColumns(c, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds all of the field names passed into the "to" parameter of the
|
||||
* constructor with their corresponding cursor columns as specified in the
|
||||
* "from" parameter.
|
||||
*
|
||||
* Binding occurs in two phases. First, if a
|
||||
* {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
|
||||
* {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
|
||||
* is invoked. If the returned value is true, binding has occured. If the
|
||||
* returned value is false and the view to bind is a TextView,
|
||||
* {@link #setViewText(TextView, String)} is invoked. If the returned value is
|
||||
* false and the view to bind is an ImageView,
|
||||
* {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
|
||||
* binding can be found, an {@link IllegalStateException} is thrown.
|
||||
*
|
||||
* @throws IllegalStateException if binding cannot occur
|
||||
*
|
||||
* @see android.widget.CursorAdapter#bindView(android.view.View,
|
||||
* android.content.Context, android.database.Cursor)
|
||||
* @see #getViewBinder()
|
||||
* @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)
|
||||
* @see #setViewImage(ImageView, String)
|
||||
* @see #setViewText(TextView, String)
|
||||
*/
|
||||
@Override
|
||||
public void bindView(View view, Context context, Cursor cursor) {
|
||||
final ViewBinder binder = mViewBinder;
|
||||
final int count = mTo.length;
|
||||
final int[] from = mFrom;
|
||||
final int[] to = mTo;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View v = view.findViewById(to[i]);
|
||||
if (v != null) {
|
||||
boolean bound = false;
|
||||
if (binder != null) {
|
||||
bound = binder.setViewValue(v, cursor, from[i]);
|
||||
}
|
||||
|
||||
if (!bound) {
|
||||
String text = cursor.getString(from[i]);
|
||||
if (text == null) {
|
||||
text = "";
|
||||
}
|
||||
|
||||
if (v instanceof TextView) {
|
||||
setViewText((TextView) v, text);
|
||||
} else if (v instanceof ImageView) {
|
||||
setViewImage((ImageView) v, text);
|
||||
} else {
|
||||
throw new IllegalStateException(v.getClass().getName() + " is not a " +
|
||||
" view that can be bounds by this SimpleCursorAdapter");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ViewBinder} used to bind data to views.
|
||||
*
|
||||
* @return a ViewBinder or null if the binder does not exist
|
||||
*
|
||||
* @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
|
||||
* @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)
|
||||
*/
|
||||
public ViewBinder getViewBinder() {
|
||||
return mViewBinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the binder used to bind data to views.
|
||||
*
|
||||
* @param viewBinder the binder used to bind data to views, can be null to
|
||||
* remove the existing binder
|
||||
*
|
||||
* @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
|
||||
* @see #getViewBinder()
|
||||
*/
|
||||
public void setViewBinder(ViewBinder viewBinder) {
|
||||
mViewBinder = viewBinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by bindView() to set the image for an ImageView but only if
|
||||
* there is no existing ViewBinder or if the existing ViewBinder cannot
|
||||
* handle binding to an ImageView.
|
||||
*
|
||||
* By default, the value will be treated as an image resource. If the
|
||||
* value cannot be used as an image resource, the value is used as an
|
||||
* image Uri.
|
||||
*
|
||||
* Intended to be overridden by Adapters that need to filter strings
|
||||
* retrieved from the database.
|
||||
*
|
||||
* @param v ImageView to receive an image
|
||||
* @param value the value retrieved from the cursor
|
||||
*/
|
||||
public void setViewImage(ImageView v, String value) {
|
||||
try {
|
||||
v.setImageResource(Integer.parseInt(value));
|
||||
} catch (NumberFormatException nfe) {
|
||||
v.setImageURI(Uri.parse(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by bindView() to set the text for a TextView but only if
|
||||
* there is no existing ViewBinder or if the existing ViewBinder cannot
|
||||
* handle binding to a TextView.
|
||||
*
|
||||
* Intended to be overridden by Adapters that need to filter strings
|
||||
* retrieved from the database.
|
||||
*
|
||||
* @param v TextView to receive text
|
||||
* @param text the text to be set for the TextView
|
||||
*/
|
||||
public void setViewText(TextView v, String text) {
|
||||
v.setText(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index of the column used to get a String representation
|
||||
* of the Cursor.
|
||||
*
|
||||
* @return a valid index in the current Cursor or -1
|
||||
*
|
||||
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
|
||||
* @see #setStringConversionColumn(int)
|
||||
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
|
||||
* @see #getCursorToStringConverter()
|
||||
*/
|
||||
public int getStringConversionColumn() {
|
||||
return mStringConversionColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the index of the column in the Cursor used to get a String
|
||||
* representation of that Cursor. The column is used to convert the
|
||||
* Cursor to a String only when the current CursorToStringConverter
|
||||
* is null.
|
||||
*
|
||||
* @param stringConversionColumn a valid index in the current Cursor or -1 to use the default
|
||||
* conversion mechanism
|
||||
*
|
||||
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
|
||||
* @see #getStringConversionColumn()
|
||||
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
|
||||
* @see #getCursorToStringConverter()
|
||||
*/
|
||||
public void setStringConversionColumn(int stringConversionColumn) {
|
||||
mStringConversionColumn = stringConversionColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the converter used to convert the filtering Cursor
|
||||
* into a String.
|
||||
*
|
||||
* @return null if the converter does not exist or an instance of
|
||||
* {@link android.widget.SimpleCursorAdapter.CursorToStringConverter}
|
||||
*
|
||||
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
|
||||
* @see #getStringConversionColumn()
|
||||
* @see #setStringConversionColumn(int)
|
||||
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
|
||||
*/
|
||||
public CursorToStringConverter getCursorToStringConverter() {
|
||||
return mCursorToStringConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the converter used to convert the filtering Cursor
|
||||
* into a String.
|
||||
*
|
||||
* @param cursorToStringConverter the Cursor to String converter, or
|
||||
* null to remove the converter
|
||||
*
|
||||
* @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
|
||||
* @see #getStringConversionColumn()
|
||||
* @see #setStringConversionColumn(int)
|
||||
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
|
||||
*/
|
||||
public void setCursorToStringConverter(CursorToStringConverter cursorToStringConverter) {
|
||||
mCursorToStringConverter = cursorToStringConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a CharSequence representation of the specified Cursor as defined
|
||||
* by the current CursorToStringConverter. If no CursorToStringConverter
|
||||
* has been set, the String conversion column is used instead. If the
|
||||
* conversion column is -1, the returned String is empty if the cursor
|
||||
* is null or Cursor.toString().
|
||||
*
|
||||
* @param cursor the Cursor to convert to a CharSequence
|
||||
*
|
||||
* @return a non-null CharSequence representing the cursor
|
||||
*/
|
||||
@Override
|
||||
public CharSequence convertToString(Cursor cursor) {
|
||||
if (mCursorToStringConverter != null) {
|
||||
return mCursorToStringConverter.convertToString(cursor);
|
||||
} else if (mStringConversionColumn > -1) {
|
||||
return cursor.getString(mStringConversionColumn);
|
||||
}
|
||||
|
||||
return super.convertToString(cursor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a map from an array of strings to an array of column-id integers in cursor c.
|
||||
* If c is null, the array will be discarded.
|
||||
*
|
||||
* @param c the cursor to find the columns from
|
||||
* @param from the Strings naming the columns of interest
|
||||
*/
|
||||
private void findColumns(Cursor c, String[] from) {
|
||||
if (c != null) {
|
||||
int i;
|
||||
int count = from.length;
|
||||
if (mFrom == null || mFrom.length != count) {
|
||||
mFrom = new int[count];
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
mFrom[i] = c.getColumnIndexOrThrow(from[i]);
|
||||
}
|
||||
} else {
|
||||
mFrom = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor swapCursor(Cursor c) {
|
||||
// super.swapCursor() will notify observers before we have
|
||||
// a valid mapping, make sure we have a mapping before this
|
||||
// happens
|
||||
findColumns(c, mOriginalFrom);
|
||||
return super.swapCursor(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the cursor and change the column-to-view mappings at the same time.
|
||||
*
|
||||
* @param c The database cursor. Can be null if the cursor is not available yet.
|
||||
* @param from A list of column names representing the data to bind to the UI. Can be null
|
||||
* if the cursor is not available yet.
|
||||
* @param to The views that should display column in the "from" parameter.
|
||||
* These should all be TextViews. The first N views in this list
|
||||
* are given the values of the first N columns in the from
|
||||
* parameter. Can be null if the cursor is not available yet.
|
||||
*/
|
||||
public void changeCursorAndColumns(Cursor c, String[] from, int[] to) {
|
||||
mOriginalFrom = from;
|
||||
mTo = to;
|
||||
// super.changeCursor() will notify observers before we have
|
||||
// a valid mapping, make sure we have a mapping before this
|
||||
// happens
|
||||
findColumns(c, mOriginalFrom);
|
||||
super.changeCursor(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* This class can be used by external clients of SimpleCursorAdapter
|
||||
* to bind values fom the Cursor to views.
|
||||
*
|
||||
* You should use this class to bind values from the Cursor to views
|
||||
* that are not directly supported by SimpleCursorAdapter or to
|
||||
* change the way binding occurs for views supported by
|
||||
* SimpleCursorAdapter.
|
||||
*
|
||||
* @see SimpleCursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor)
|
||||
* @see SimpleCursorAdapter#setViewImage(ImageView, String)
|
||||
* @see SimpleCursorAdapter#setViewText(TextView, String)
|
||||
*/
|
||||
public static interface ViewBinder {
|
||||
/**
|
||||
* Binds the Cursor column defined by the specified index to the specified view.
|
||||
*
|
||||
* When binding is handled by this ViewBinder, this method must return true.
|
||||
* If this method returns false, SimpleCursorAdapter will attempts to handle
|
||||
* the binding on its own.
|
||||
*
|
||||
* @param view the view to bind the data to
|
||||
* @param cursor the cursor to get the data from
|
||||
* @param columnIndex the column at which the data can be found in the cursor
|
||||
*
|
||||
* @return true if the data was bound to the view, false otherwise
|
||||
*/
|
||||
boolean setViewValue(View view, Cursor cursor, int columnIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* This class can be used by external clients of SimpleCursorAdapter
|
||||
* to define how the Cursor should be converted to a String.
|
||||
*
|
||||
* @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
|
||||
*/
|
||||
public static interface CursorToStringConverter {
|
||||
/**
|
||||
* Returns a CharSequence representing the specified Cursor.
|
||||
*
|
||||
* @param cursor the cursor for which a CharSequence representation
|
||||
* is requested
|
||||
*
|
||||
* @return a non-null CharSequence representing the cursor
|
||||
*/
|
||||
CharSequence convertToString(Cursor cursor);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package com.mobeta.android.dslv;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Color;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ImageView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Simple implementation of the FloatViewManager class. Uses list
|
||||
* items as they appear in the ListView to create the floating View.
|
||||
*/
|
||||
public class SimpleFloatViewManager implements DragSortListView.FloatViewManager {
|
||||
|
||||
private Bitmap mFloatBitmap;
|
||||
|
||||
private ImageView mImageView;
|
||||
|
||||
private int mFloatBGColor = Color.BLACK;
|
||||
|
||||
private ListView mListView;
|
||||
|
||||
public SimpleFloatViewManager(ListView lv) {
|
||||
mListView = lv;
|
||||
}
|
||||
|
||||
public void setBackgroundColor(int color) {
|
||||
mFloatBGColor = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* This simple implementation creates a Bitmap copy of the
|
||||
* list item currently shown at ListView <code>position</code>.
|
||||
*/
|
||||
@Override
|
||||
public View onCreateFloatView(int position) {
|
||||
// Guaranteed that this will not be null? I think so. Nope, got
|
||||
// a NullPointerException once...
|
||||
View v = mListView.getChildAt(position + mListView.getHeaderViewsCount() - mListView.getFirstVisiblePosition());
|
||||
|
||||
if (v == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
v.setPressed(false);
|
||||
|
||||
// Create a copy of the drawing cache so that it does not get
|
||||
// recycled by the framework when the list tries to clean up memory
|
||||
//v.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
|
||||
v.setDrawingCacheEnabled(true);
|
||||
mFloatBitmap = Bitmap.createBitmap(v.getDrawingCache());
|
||||
v.setDrawingCacheEnabled(false);
|
||||
|
||||
if (mImageView == null) {
|
||||
mImageView = new ImageView(mListView.getContext());
|
||||
}
|
||||
mImageView.setBackgroundColor(mFloatBGColor);
|
||||
mImageView.setPadding(0, 0, 0, 0);
|
||||
mImageView.setImageBitmap(mFloatBitmap);
|
||||
mImageView.setLayoutParams(new ViewGroup.LayoutParams(v.getWidth(), v.getHeight()));
|
||||
|
||||
return mImageView;
|
||||
}
|
||||
|
||||
/**
|
||||
* This does nothing
|
||||
*/
|
||||
@Override
|
||||
public void onDragFloatView(View floatView, Point position, Point touch) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the Bitmap from the ImageView created in
|
||||
* onCreateFloatView() and tells the system to recycle it.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroyFloatView(View floatView) {
|
||||
((ImageView) floatView).setImageDrawable(null);
|
||||
|
||||
mFloatBitmap.recycle();
|
||||
mFloatBitmap = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,40 +21,40 @@ import android.graphics.Color;
|
||||
public class ColorHelper
|
||||
{
|
||||
public static final int[] palette =
|
||||
{
|
||||
Color.parseColor("#D32F2F"), // red
|
||||
{
|
||||
Color.parseColor("#D32F2F"), // red
|
||||
Color.parseColor("#E64A19"), // orange
|
||||
Color.parseColor("#F9A825"), // yellow
|
||||
Color.parseColor("#F9A825"), // yellow
|
||||
Color.parseColor("#AFB42B"), // light green
|
||||
Color.parseColor("#388E3C"), // dark green
|
||||
Color.parseColor("#388E3C"), // dark green
|
||||
Color.parseColor("#00897B"), // teal
|
||||
Color.parseColor("#00ACC1"), // cyan
|
||||
Color.parseColor("#00ACC1"), // cyan
|
||||
Color.parseColor("#039BE5"), // blue
|
||||
Color.parseColor("#5E35B1"), // deep purple
|
||||
Color.parseColor("#5E35B1"), // deep purple
|
||||
Color.parseColor("#8E24AA"), // purple
|
||||
Color.parseColor("#D81B60"), // pink
|
||||
Color.parseColor("#D81B60"), // pink
|
||||
Color.parseColor("#303030"), // dark grey
|
||||
Color.parseColor("#aaaaaa") // light grey
|
||||
};
|
||||
Color.parseColor("#aaaaaa") // light grey
|
||||
};
|
||||
|
||||
public static int mixColors(int color1, int color2, float amount)
|
||||
{
|
||||
final byte ALPHA_CHANNEL = 24;
|
||||
final byte RED_CHANNEL = 16;
|
||||
final byte GREEN_CHANNEL = 8;
|
||||
final byte BLUE_CHANNEL = 0;
|
||||
{
|
||||
final byte ALPHA_CHANNEL = 24;
|
||||
final byte RED_CHANNEL = 16;
|
||||
final byte GREEN_CHANNEL = 8;
|
||||
final byte BLUE_CHANNEL = 0;
|
||||
|
||||
final float inverseAmount = 1.0f - amount;
|
||||
final float inverseAmount = 1.0f - amount;
|
||||
|
||||
int a = ((int) (((float) (color1 >> ALPHA_CHANNEL & 0xff) * amount) +
|
||||
((float) (color2 >> ALPHA_CHANNEL & 0xff) * inverseAmount))) & 0xff;
|
||||
int r = ((int) (((float) (color1 >> RED_CHANNEL & 0xff) * amount) +
|
||||
((float) (color2 >> RED_CHANNEL & 0xff) * inverseAmount))) & 0xff;
|
||||
int g = ((int) (((float) (color1 >> GREEN_CHANNEL & 0xff) * amount) +
|
||||
((float) (color2 >> GREEN_CHANNEL & 0xff) * inverseAmount))) & 0xff;
|
||||
int b = ((int) (((float) (color1 & 0xff) * amount) +
|
||||
((float) (color2 & 0xff) * inverseAmount))) & 0xff;
|
||||
int a = ((int) (((float) (color1 >> ALPHA_CHANNEL & 0xff) * amount) +
|
||||
((float) (color2 >> ALPHA_CHANNEL & 0xff) * inverseAmount))) & 0xff;
|
||||
int r = ((int) (((float) (color1 >> RED_CHANNEL & 0xff) * amount) +
|
||||
((float) (color2 >> RED_CHANNEL & 0xff) * inverseAmount))) & 0xff;
|
||||
int g = ((int) (((float) (color1 >> GREEN_CHANNEL & 0xff) * amount) +
|
||||
((float) (color2 >> GREEN_CHANNEL & 0xff) * inverseAmount))) & 0xff;
|
||||
int b = ((int) (((float) (color1 & 0xff) * amount) +
|
||||
((float) (color2 & 0xff) * inverseAmount))) & 0xff;
|
||||
|
||||
return a << ALPHA_CHANNEL | r << RED_CHANNEL | g << GREEN_CHANNEL | b << BLUE_CHANNEL;
|
||||
}
|
||||
return a << ALPHA_CHANNEL | r << RED_CHANNEL | g << GREEN_CHANNEL | b << BLUE_CHANNEL;
|
||||
}
|
||||
}
|
||||
@@ -16,101 +16,173 @@
|
||||
|
||||
package org.isoron.helpers;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import android.content.Context;
|
||||
import android.text.format.DateFormat;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class DateHelper
|
||||
{
|
||||
public static int millisecondsInOneDay = 24 * 60 * 60 * 1000;
|
||||
|
||||
public static long getLocalTime()
|
||||
{
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
long now = new Date().getTime();
|
||||
return now + tz.getOffset(now);
|
||||
}
|
||||
|
||||
public static long getStartOfDay(long timestamp)
|
||||
{
|
||||
return (timestamp / millisecondsInOneDay) * millisecondsInOneDay;
|
||||
}
|
||||
|
||||
public static long getStartOfToday()
|
||||
{
|
||||
return getStartOfDay(DateHelper.getLocalTime());
|
||||
}
|
||||
public static int millisecondsInOneDay = 24 * 60 * 60 * 1000;
|
||||
|
||||
// public static Date getStartOfDay(Date date)
|
||||
// {
|
||||
// Calendar calendar = Calendar.getInstance();
|
||||
// calendar.setTime(date);
|
||||
// calendar.set(Calendar.HOUR_OF_DAY, 0);
|
||||
// calendar.set(Calendar.MINUTE, 0);
|
||||
// calendar.set(Calendar.SECOND, 0);
|
||||
// calendar.set(Calendar.MILLISECOND, 0);
|
||||
// return calendar.getTime();
|
||||
// }
|
||||
public static long getLocalTime()
|
||||
{
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
long now = new Date().getTime();
|
||||
return now + tz.getOffset(now);
|
||||
}
|
||||
|
||||
public static int differenceInDays(Date from, Date to)
|
||||
{
|
||||
long milliseconds = getStartOfDay(to.getTime()) - getStartOfDay(from.getTime());
|
||||
public static long toLocalTime(long timestamp)
|
||||
{
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
long now = new Date(timestamp).getTime();
|
||||
return now + tz.getOffset(now);
|
||||
}
|
||||
|
||||
public static long getStartOfDay(long timestamp)
|
||||
{
|
||||
return (timestamp / millisecondsInOneDay) * millisecondsInOneDay;
|
||||
}
|
||||
|
||||
public static GregorianCalendar getStartOfTodayCalendar()
|
||||
{
|
||||
GregorianCalendar day = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
day.setTimeInMillis(DateHelper.getStartOfDay(DateHelper.getLocalTime()));
|
||||
return day;
|
||||
}
|
||||
|
||||
public static GregorianCalendar getCalendar(long timestamp)
|
||||
{
|
||||
GregorianCalendar day = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
day.setTimeInMillis(timestamp);
|
||||
return day;
|
||||
}
|
||||
|
||||
public static int getWeekday(long timestamp)
|
||||
{
|
||||
GregorianCalendar day = getCalendar(timestamp);
|
||||
return day.get(GregorianCalendar.DAY_OF_WEEK) % 7;
|
||||
}
|
||||
|
||||
public static long getStartOfToday()
|
||||
{
|
||||
return getStartOfDay(DateHelper.getLocalTime());
|
||||
}
|
||||
|
||||
public static String formatTime(Context context, int hours, int minutes)
|
||||
{
|
||||
int reminderMilliseconds = (hours * 60 + minutes) * 60 * 1000;
|
||||
|
||||
Date date = new Date(reminderMilliseconds);
|
||||
java.text.DateFormat df = DateFormat.getTimeFormat(context);
|
||||
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
return df.format(date);
|
||||
}
|
||||
|
||||
public static String formatHeaderDate(GregorianCalendar day)
|
||||
{
|
||||
String dayOfMonth = Integer.toString(day.get(GregorianCalendar.DAY_OF_MONTH));
|
||||
String dayOfWeek = day.getDisplayName(GregorianCalendar.DAY_OF_WEEK,
|
||||
GregorianCalendar.SHORT, Locale.getDefault());
|
||||
|
||||
return dayOfWeek + "\n" + dayOfMonth;
|
||||
}
|
||||
|
||||
public static int differenceInDays(Date from, Date to)
|
||||
{
|
||||
long milliseconds = getStartOfDay(to.getTime()) - getStartOfDay(from.getTime());
|
||||
return (int) (milliseconds / millisecondsInOneDay);
|
||||
}
|
||||
}
|
||||
|
||||
public static String differenceInWords(Date from, Date to)
|
||||
{
|
||||
Integer days = differenceInDays(from, to);
|
||||
boolean negative = (days < 0);
|
||||
days = Math.abs(days);
|
||||
public static String[] getShortDayNames()
|
||||
{
|
||||
return getDayNames(GregorianCalendar.SHORT);
|
||||
}
|
||||
|
||||
Integer weeks = (int) Math.round(days / 7.0);
|
||||
Double months = days / 30.4;
|
||||
Double years = days / 365.0;
|
||||
public static String[] getLongDayNames()
|
||||
{
|
||||
return getDayNames(GregorianCalendar.LONG);
|
||||
}
|
||||
|
||||
StringBuffer s = new StringBuffer();
|
||||
DecimalFormat df = new DecimalFormat("#.#");
|
||||
|
||||
if(months > 18)
|
||||
{
|
||||
s.append(df.format(years));
|
||||
s.append(" years");
|
||||
}
|
||||
else if(weeks > 6)
|
||||
{
|
||||
s.append(df.format(months));
|
||||
s.append(" months");
|
||||
}
|
||||
else if(days > 13)
|
||||
{
|
||||
s.append(weeks);
|
||||
s.append(" weeks");
|
||||
}
|
||||
else if(days > 6)
|
||||
{
|
||||
s.append(days);
|
||||
s.append(" days");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(days == 0)
|
||||
s.append("Today");
|
||||
else if(days == 1 && negative)
|
||||
s.append("Yesterday");
|
||||
else if(days == 1 && !negative)
|
||||
s.append("Tomorrow");
|
||||
else
|
||||
{
|
||||
if(negative)
|
||||
s.append("past ");
|
||||
s.append(new SimpleDateFormat("EEEE").format(to));
|
||||
}
|
||||
}
|
||||
public static String[] getDayNames(int format)
|
||||
{
|
||||
String[] wdays = new String[7];
|
||||
|
||||
if(negative && days > 6)
|
||||
s.append(" ago");
|
||||
GregorianCalendar day = new GregorianCalendar();
|
||||
day.set(GregorianCalendar.DAY_OF_WEEK, 0);
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
wdays[i] = day.getDisplayName(GregorianCalendar.DAY_OF_WEEK, format,
|
||||
Locale.getDefault());
|
||||
day.add(GregorianCalendar.DAY_OF_MONTH, 1);
|
||||
}
|
||||
|
||||
return wdays;
|
||||
}
|
||||
|
||||
public static String formatWeekdayList(Context context, boolean weekday[])
|
||||
{
|
||||
String shortDayNames[] = getShortDayNames();
|
||||
String longDayNames[] = getLongDayNames();
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
|
||||
int count = 0;
|
||||
int first = 0;
|
||||
boolean isFirst = true;
|
||||
for(int i = 0; i < 7; i++)
|
||||
{
|
||||
if(weekday[i])
|
||||
{
|
||||
if(isFirst) first = i;
|
||||
else buffer.append(", ");
|
||||
|
||||
buffer.append(shortDayNames[i]);
|
||||
isFirst = false;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if(count == 1) return longDayNames[first];
|
||||
if(count == 2 && weekday[0] && weekday[1]) return context.getString(R.string.weekends);
|
||||
if(count == 5 && !weekday[0] && !weekday[1]) return context.getString(R.string.any_weekday);
|
||||
if(count == 7) return context.getString(R.string.any_day);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
public static Integer packWeekdayList(boolean weekday[])
|
||||
{
|
||||
int list = 0;
|
||||
int current = 1;
|
||||
|
||||
for(int i = 0; i < 7; i++)
|
||||
{
|
||||
if(weekday[i]) list |= current;
|
||||
current = current << 1;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static boolean[] unpackWeekdayList(int list)
|
||||
{
|
||||
boolean[] weekday = new boolean[7];
|
||||
int current = 1;
|
||||
|
||||
for(int i = 0; i < 7; i++)
|
||||
{
|
||||
if((list & current) != 0) weekday[i] = true;
|
||||
current = current << 1;
|
||||
}
|
||||
|
||||
return weekday;
|
||||
}
|
||||
|
||||
return s.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,39 +19,50 @@ package org.isoron.helpers;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Vibrator;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
public abstract class DialogHelper
|
||||
{
|
||||
|
||||
// public static AlertDialog alert(Activity context, String title, String message, OnClickListener positiveClickListener) {
|
||||
// return new AlertDialog.Builder(context)
|
||||
// .setTitle(title)
|
||||
// .setMessage(message)
|
||||
// .setPositiveButton(android.R.string.yes, positiveClickListener)
|
||||
// .setNegativeButton(android.R.string.no, null).show();
|
||||
// }
|
||||
private static Typeface fontawesome;
|
||||
|
||||
public static abstract class SimpleClickListener implements OnClickListener
|
||||
{
|
||||
public abstract void onClick();
|
||||
public interface OnSavedListener
|
||||
{
|
||||
void onSaved(Command command, Object savedObject);
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int whichButton)
|
||||
{
|
||||
onClick();
|
||||
}
|
||||
}
|
||||
public static void showSoftKeyboard(View view)
|
||||
{
|
||||
InputMethodManager imm = (InputMethodManager) view.getContext()
|
||||
.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
public static interface OnSavedListener
|
||||
{
|
||||
public void onSaved(Command command, Object savedObject);
|
||||
}
|
||||
public static void vibrate(Context context, int duration)
|
||||
{
|
||||
Vibrator vb = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
vb.vibrate(duration);
|
||||
}
|
||||
|
||||
public static void showSoftKeyboard(View view)
|
||||
{
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
public static void incrementLaunchCount(Context context)
|
||||
{
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
int count = prefs.getInt("launch_count", 0);
|
||||
prefs.edit().putInt("launch_count", count + 1).apply();
|
||||
}
|
||||
|
||||
public static int getLaunchCount(Context context)
|
||||
{
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
return prefs.getInt("launch_count", 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +81,7 @@ abstract public class ReplayableActivity extends Activity
|
||||
toast.show();
|
||||
}
|
||||
|
||||
public void executeCommand(final Command command, Boolean clearRedoStack,
|
||||
final Long refreshKey)
|
||||
public void executeCommand(final Command command, Boolean clearRedoStack, final Long refreshKey)
|
||||
{
|
||||
undoList.push(command);
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
|
||||
@@ -30,27 +29,26 @@ public class IntroActivity extends AppIntro2
|
||||
{
|
||||
showStatusBar(false);
|
||||
|
||||
addSlide(AppIntroFragment.newInstance("Welcome",
|
||||
"Habits Tracker helps you create and maintain good habits.", R.drawable.tutorial_1,
|
||||
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_1),
|
||||
getString(R.string.intro_description_1), R.drawable.intro_icon_1,
|
||||
Color.parseColor("#194673")));
|
||||
|
||||
addSlide(AppIntroFragment.newInstance("Create some new habits",
|
||||
"Every day, after performing your habit, put a checkmark on the app.",
|
||||
R.drawable.tutorial_2, Color.parseColor("#ffa726")));
|
||||
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_2),
|
||||
getString(R.string.intro_description_2), R.drawable.intro_icon_2,
|
||||
Color.parseColor("#ffa726")));
|
||||
|
||||
addSlide(AppIntroFragment.newInstance("Keep doing it",
|
||||
"Habits performed consistently for a long time will earn a full star.",
|
||||
R.drawable.tutorial_3, Color.parseColor("#7cb342")));
|
||||
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_3),
|
||||
getString(R.string.intro_description_3), R.drawable.intro_icon_3,
|
||||
Color.parseColor("#7cb342")));
|
||||
|
||||
addSlide(AppIntroFragment.newInstance("Track your progress",
|
||||
"Detailed graphs show you how your habits improved over time.",
|
||||
R.drawable.tutorial_4, Color.parseColor("#9575cd")));
|
||||
addSlide(AppIntroFragment.newInstance(getString(R.string.intro_title_4),
|
||||
getString(R.string.intro_description_4), R.drawable.intro_icon_4,
|
||||
Color.parseColor("#9575cd")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNextPressed()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,6 +60,5 @@ public class IntroActivity extends AppIntro2
|
||||
@Override
|
||||
public void onSlideChanged()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,18 @@ import android.preference.PreferenceManager;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.helpers.ReplayableActivity;
|
||||
import org.isoron.uhabits.dialogs.ListHabitsFragment;
|
||||
import org.isoron.uhabits.fragments.ListHabitsFragment;
|
||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
public class MainActivity extends ReplayableActivity
|
||||
implements ListHabitsFragment.OnHabitClickListener
|
||||
{
|
||||
private ListHabitsFragment listHabitsFragment;
|
||||
SharedPreferences prefs;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
@@ -39,26 +43,30 @@ public class MainActivity extends ReplayableActivity
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.list_habits_activity);
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
listHabitsFragment =
|
||||
(ListHabitsFragment) getFragmentManager().findFragmentById(R.id.fragment1);
|
||||
|
||||
onStartup();
|
||||
}
|
||||
|
||||
private void onStartup()
|
||||
{
|
||||
PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
||||
|
||||
listHabitsFragment = (ListHabitsFragment) getFragmentManager().findFragmentById(
|
||||
R.id.fragment1);
|
||||
|
||||
ReminderHelper.createReminderAlarms(MainActivity.this);
|
||||
|
||||
DialogHelper.incrementLaunchCount(this);
|
||||
showTutorial();
|
||||
|
||||
}
|
||||
|
||||
private void showTutorial()
|
||||
{
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
Boolean firstRun = prefs.getBoolean("pref_first_run", true);
|
||||
|
||||
if(firstRun)
|
||||
if (firstRun)
|
||||
{
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putBoolean("pref_first_run", false);
|
||||
editor.putLong("last_hint_timestamp", DateHelper.getStartOfToday()).apply();
|
||||
editor.apply();
|
||||
|
||||
Intent intent = new Intent(this, IntroActivity.class);
|
||||
|
||||
@@ -28,40 +28,63 @@ import android.content.SharedPreferences;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
{
|
||||
|
||||
public static String ACTION_CHECK = "org.isoron.uhabits.ACTION_CHECK";
|
||||
public static String ACTION_DISMISS = "org.isoron.uhabits.ACTION_DISMISS";
|
||||
public static String ACTION_REMIND = "org.isoron.uhabits.ACTION_REMIND";
|
||||
public static String ACTION_REMOVE_REMINDER = "org.isoron.uhabits.ACTION_REMOVE_REMINDER";
|
||||
public static String ACTION_SNOOZE = "org.isoron.uhabits.ACTION_SNOOZE";
|
||||
public static final String ACTION_CHECK = "org.isoron.uhabits.ACTION_CHECK";
|
||||
public static final String ACTION_DISMISS = "org.isoron.uhabits.ACTION_DISMISS";
|
||||
public static final String ACTION_REMIND = "org.isoron.uhabits.ACTION_REMIND";
|
||||
public static final String ACTION_REMOVE_REMINDER = "org.isoron.uhabits.ACTION_REMOVE_REMINDER";
|
||||
public static final String ACTION_SNOOZE = "org.isoron.uhabits.ACTION_SNOOZE";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
public void onReceive(final Context context, Intent intent)
|
||||
{
|
||||
String action = intent.getAction();
|
||||
switch (intent.getAction())
|
||||
{
|
||||
case ACTION_REMIND:
|
||||
createNotification(context, intent);
|
||||
createReminderAlarms(context);
|
||||
break;
|
||||
|
||||
if (action.equals(ACTION_REMIND)) createNotification(context, intent.getData());
|
||||
case ACTION_DISMISS:
|
||||
dismissAllHabits();
|
||||
break;
|
||||
|
||||
else if (action.equals(ACTION_DISMISS)) dismissAllHabits();
|
||||
case ACTION_CHECK:
|
||||
checkHabit(context, intent);
|
||||
break;
|
||||
|
||||
else if (action.equals(ACTION_CHECK)) checkHabit(context, intent.getData());
|
||||
|
||||
else if (action.equals(ACTION_SNOOZE)) snoozeHabit(context, intent.getData());
|
||||
case ACTION_SNOOZE:
|
||||
snoozeHabit(context, intent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void snoozeHabit(Context context, Uri data)
|
||||
private void createReminderAlarms(final Context context)
|
||||
{
|
||||
new Handler().postDelayed(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
ReminderHelper.createReminderAlarms(context);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
private void snoozeHabit(Context context, Intent intent)
|
||||
{
|
||||
Uri data = intent.getData();
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
long delayMinutes = Long.parseLong(prefs.getString("pref_snooze_interval", "15"));
|
||||
|
||||
@@ -71,10 +94,16 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
dismissNotification(context, habit);
|
||||
}
|
||||
|
||||
private void checkHabit(Context context, Uri data)
|
||||
private void checkHabit(Context context, Intent intent)
|
||||
{
|
||||
Uri data = intent.getData();
|
||||
Long timestamp = DateHelper.getStartOfToday();
|
||||
String paramTimestamp = data.getQueryParameter("timestamp");
|
||||
|
||||
if(paramTimestamp != null) timestamp = Long.parseLong(paramTimestamp);
|
||||
|
||||
Habit habit = Habit.get(ContentUris.parseId(data));
|
||||
habit.toggleRepetitionToday();
|
||||
habit.toggleRepetition(timestamp);
|
||||
habit.save();
|
||||
dismissNotification(context, habit);
|
||||
}
|
||||
@@ -98,19 +127,20 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
}
|
||||
|
||||
|
||||
private void createNotification(Context context, Uri data)
|
||||
private void createNotification(Context context, Intent intent)
|
||||
{
|
||||
|
||||
Uri data = intent.getData();
|
||||
Habit habit = Habit.get(ContentUris.parseId(data));
|
||||
|
||||
if (habit.hasImplicitRepToday()) return;
|
||||
|
||||
Log.d("Alarm", String.format("Applying highlight: %s", habit.name));
|
||||
habit.highlight = 1;
|
||||
habit.save();
|
||||
|
||||
if (!checkWeekday(intent, habit)) return;
|
||||
|
||||
// Check if reminder has been turned off after alarm was scheduled
|
||||
if (habit.reminder_hour == null) return;
|
||||
if (habit.reminderHour == null) return;
|
||||
|
||||
Intent contentIntent = new Intent(context, MainActivity.class);
|
||||
contentIntent.setData(data);
|
||||
@@ -133,6 +163,8 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
|
||||
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||
|
||||
Long reminderTime = intent.getLongExtra("reminderTime", DateHelper.getStartOfToday());
|
||||
|
||||
NotificationCompat.WearableExtender wearableExtender =
|
||||
new NotificationCompat.WearableExtender().setBackground(
|
||||
BitmapFactory.decodeResource(context.getResources(), R.drawable.stripe));
|
||||
@@ -143,10 +175,14 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
.setContentText(habit.description)
|
||||
.setContentIntent(contentPendingIntent)
|
||||
.setDeleteIntent(deletePendingIntent)
|
||||
.addAction(R.drawable.ic_action_check, "Check", checkIntentPending)
|
||||
.addAction(R.drawable.ic_action_snooze, "Later", snoozeIntentPending)
|
||||
.addAction(R.drawable.ic_action_check,
|
||||
context.getString(R.string.check), checkIntentPending)
|
||||
.addAction(R.drawable.ic_action_snooze,
|
||||
context.getString(R.string.snooze), snoozeIntentPending)
|
||||
.setSound(soundUri)
|
||||
.extend(wearableExtender)
|
||||
.setWhen(reminderTime)
|
||||
.setShowWhen(true)
|
||||
.build();
|
||||
|
||||
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
@@ -158,4 +194,14 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
notificationManager.notify(notificationId, notification);
|
||||
}
|
||||
|
||||
private boolean checkWeekday(Intent intent, Habit habit)
|
||||
{
|
||||
Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
|
||||
|
||||
boolean reminderDays[] = DateHelper.unpackWeekdayList(habit.reminderDays);
|
||||
int weekday = DateHelper.getWeekday(timestamp);
|
||||
|
||||
return reminderDays[weekday];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ package org.isoron.uhabits;
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.isoron.uhabits.dialogs.SettingsFragment;
|
||||
import org.isoron.uhabits.fragments.SettingsFragment;
|
||||
|
||||
public class SettingsActivity extends Activity
|
||||
{
|
||||
@@ -27,7 +27,8 @@ public class SettingsActivity extends Activity
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
getFragmentManager().beginTransaction().replace(android.R.id.content,
|
||||
new SettingsFragment()).commit();
|
||||
getFragmentManager().beginTransaction()
|
||||
.replace(android.R.id.content, new SettingsFragment())
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
package org.isoron.uhabits;
|
||||
|
||||
import org.isoron.helpers.ReplayableActivity;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentUris;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
@@ -28,6 +24,9 @@ import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.isoron.helpers.ReplayableActivity;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
public class ShowHabitActivity extends ReplayableActivity
|
||||
{
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/* Copyright (C) 2016 Alinson Santos Xavier
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.commands;
|
||||
|
||||
import org.isoron.helpers.Command;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ArchiveHabitsCommand extends Command
|
||||
{
|
||||
|
||||
private List<Habit> habits;
|
||||
|
||||
public ArchiveHabitsCommand(Habit habit)
|
||||
{
|
||||
habits = new LinkedList<>();
|
||||
habits.add(habit);
|
||||
}
|
||||
|
||||
public ArchiveHabitsCommand(List<Habit> habits)
|
||||
{
|
||||
this.habits = habits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
for(Habit h : habits)
|
||||
h.archive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
for(Habit h : habits)
|
||||
h.unarchive();
|
||||
}
|
||||
|
||||
public Integer getExecuteStringId()
|
||||
{
|
||||
return R.string.toast_habit_archived;
|
||||
}
|
||||
|
||||
public Integer getUndoStringId()
|
||||
{
|
||||
return R.string.toast_habit_unarchived;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/* Copyright (C) 2016 Alinson Santos Xavier
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.commands;
|
||||
|
||||
import com.activeandroid.ActiveAndroid;
|
||||
|
||||
import org.isoron.helpers.Command;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ChangeHabitColorCommand extends Command
|
||||
{
|
||||
List<Habit> habits;
|
||||
List<Integer> originalColors;
|
||||
Integer newColor;
|
||||
|
||||
public ChangeHabitColorCommand(List<Habit> habits, Integer newColor)
|
||||
{
|
||||
this.habits = habits;
|
||||
this.newColor = newColor;
|
||||
this.originalColors = new ArrayList<>(habits.size());
|
||||
|
||||
for(Habit h : habits)
|
||||
originalColors.add(h.color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
ActiveAndroid.beginTransaction();
|
||||
|
||||
try
|
||||
{
|
||||
for(Habit h : habits)
|
||||
{
|
||||
h.color = newColor;
|
||||
h.save();
|
||||
}
|
||||
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
ActiveAndroid.beginTransaction();
|
||||
|
||||
try
|
||||
{
|
||||
int k = 0;
|
||||
for(Habit h : habits)
|
||||
{
|
||||
h.color = originalColors.get(k++);
|
||||
h.save();
|
||||
}
|
||||
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getExecuteStringId()
|
||||
{
|
||||
return R.string.toast_habit_changed;
|
||||
}
|
||||
|
||||
public Integer getUndoStringId()
|
||||
{
|
||||
return R.string.toast_habit_changed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Copyright (C) 2016 Alinson Santos Xavier
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.commands;
|
||||
|
||||
import org.isoron.helpers.Command;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DeleteHabitsCommand extends Command
|
||||
{
|
||||
private List<Habit> habits;
|
||||
|
||||
public DeleteHabitsCommand(List<Habit> habits)
|
||||
{
|
||||
this.habits = habits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
for(Habit h : habits)
|
||||
h.cascadeDelete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Integer getExecuteStringId()
|
||||
{
|
||||
return R.string.toast_habit_deleted;
|
||||
}
|
||||
|
||||
public Integer getUndoStringId()
|
||||
{
|
||||
return R.string.toast_habit_restored;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/* Copyright (C) 2016 Alinson Santos Xavier
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.commands;
|
||||
|
||||
import org.isoron.helpers.Command;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class UnarchiveHabitsCommand extends Command
|
||||
{
|
||||
|
||||
private List<Habit> habits;
|
||||
|
||||
public UnarchiveHabitsCommand(Habit habit)
|
||||
{
|
||||
habits = new LinkedList<>();
|
||||
habits.add(habit);
|
||||
}
|
||||
|
||||
public UnarchiveHabitsCommand(List<Habit> habits)
|
||||
{
|
||||
this.habits = habits;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
for(Habit h : habits)
|
||||
h.unarchive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
for(Habit h : habits)
|
||||
h.archive();
|
||||
}
|
||||
|
||||
public Integer getExecuteStringId()
|
||||
{
|
||||
return R.string.toast_habit_unarchived;
|
||||
}
|
||||
|
||||
public Integer getUndoStringId()
|
||||
{
|
||||
return R.string.toast_habit_archived;
|
||||
}
|
||||
}
|
||||
@@ -1,305 +0,0 @@
|
||||
/* Copyright (C) 2016 Alinson Santos Xavier
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.dialogs;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.Command;
|
||||
import org.isoron.helpers.DialogHelper.OnSavedListener;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import android.app.DialogFragment;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorMatrix;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.colorpicker.ColorPickerDialog;
|
||||
import com.android.colorpicker.ColorPickerSwatch;
|
||||
import com.android.datetimepicker.time.RadialPickerLayout;
|
||||
import com.android.datetimepicker.time.TimePickerDialog;
|
||||
import com.android.datetimepicker.time.TimePickerDialog.OnTimeSetListener;
|
||||
|
||||
public class EditHabitFragment extends DialogFragment implements OnClickListener
|
||||
{
|
||||
private int mode;
|
||||
static final int EDIT_MODE = 0;
|
||||
static final int CREATE_MODE = 1;
|
||||
|
||||
private OnSavedListener onSavedListener;
|
||||
|
||||
private Habit originalHabit, modified_habit;
|
||||
private TextView tvName, tvDescription, tvFreqNum, tvFreqDen, tvInputReminder;
|
||||
|
||||
private SharedPreferences prefs;
|
||||
|
||||
static class SolidColorMatrix extends ColorMatrix
|
||||
{
|
||||
public SolidColorMatrix(int color)
|
||||
{
|
||||
float matrix[] = { 0.0f, 0.0f, 0.0f, 0.0f, Color.red(color), 0.0f, 0.0f, 0.0f, 0.0f,
|
||||
Color.green(color), 0.0f, 0.0f, 0.0f, 0.0f, Color.blue(color), 0.0f, 0.0f,
|
||||
0.0f, 1.0f, 0 };
|
||||
set(matrix);
|
||||
}
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Factory *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
static EditHabitFragment editSingleHabitFragment(long id)
|
||||
{
|
||||
EditHabitFragment frag = new EditHabitFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("habitId", id);
|
||||
args.putInt("editMode", EDIT_MODE);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
static EditHabitFragment createHabitFragment()
|
||||
{
|
||||
EditHabitFragment frag = new EditHabitFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt("editMode", CREATE_MODE);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Creation *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.edit_habit, container, false);
|
||||
tvName = (TextView) view.findViewById(R.id.input_name);
|
||||
tvDescription = (TextView) view.findViewById(R.id.input_description);
|
||||
tvFreqNum = (TextView) view.findViewById(R.id.input_freq_num);
|
||||
tvFreqDen = (TextView) view.findViewById(R.id.input_freq_den);
|
||||
tvInputReminder = (TextView) view.findViewById(R.id.input_reminder_time);
|
||||
|
||||
Button buttonSave = (Button) view.findViewById(R.id.buttonSave);
|
||||
Button buttonDiscard = (Button) view.findViewById(R.id.buttonDiscard);
|
||||
|
||||
buttonSave.setOnClickListener(this);
|
||||
buttonDiscard.setOnClickListener(this);
|
||||
tvInputReminder.setOnClickListener(this);
|
||||
|
||||
ImageButton buttonPickColor = (ImageButton) view.findViewById(R.id.button_pick_color);
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
|
||||
Bundle args = getArguments();
|
||||
mode = (Integer) args.get("editMode");
|
||||
|
||||
if(mode == CREATE_MODE)
|
||||
{
|
||||
getDialog().setTitle("Create habit");
|
||||
modified_habit = new Habit();
|
||||
|
||||
int defaultNum = prefs.getInt("pref_default_habit_freq_num", modified_habit.freq_num);
|
||||
int defaultDen = prefs.getInt("pref_default_habit_freq_den", modified_habit.freq_den);
|
||||
int defaultColor = prefs.getInt("pref_default_habit_color", modified_habit.color);
|
||||
|
||||
modified_habit.color = defaultColor;
|
||||
modified_habit.freq_num = defaultNum;
|
||||
modified_habit.freq_den = defaultDen;
|
||||
}
|
||||
else if(mode == EDIT_MODE)
|
||||
{
|
||||
originalHabit = Habit.get((Long) args.get("habitId"));
|
||||
modified_habit = new Habit(originalHabit);
|
||||
|
||||
getDialog().setTitle("Edit habit");
|
||||
tvName.append(modified_habit.name);
|
||||
tvDescription.append(modified_habit.description);
|
||||
}
|
||||
|
||||
tvFreqNum.append(modified_habit.freq_num.toString());
|
||||
tvFreqDen.append(modified_habit.freq_den.toString());
|
||||
|
||||
changeColor(modified_habit.color);
|
||||
updateReminder();
|
||||
|
||||
buttonPickColor.setOnClickListener(new OnClickListener()
|
||||
{
|
||||
public void onClick(View view)
|
||||
{
|
||||
ColorPickerDialog picker = ColorPickerDialog.newInstance(
|
||||
R.string.color_picker_default_title,
|
||||
ColorHelper.palette, modified_habit.color, 4, ColorPickerDialog.SIZE_SMALL);
|
||||
|
||||
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
|
||||
{
|
||||
public void onColorSelected(int color)
|
||||
{
|
||||
changeColor(color);
|
||||
}
|
||||
});
|
||||
picker.show(getFragmentManager(), "picker");
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void changeColor(Integer color)
|
||||
{
|
||||
modified_habit.color = color;
|
||||
tvName.setTextColor(color);
|
||||
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putInt("pref_default_habit_color", color);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void updateReminder()
|
||||
{
|
||||
if(modified_habit.reminder_hour != null)
|
||||
{
|
||||
tvInputReminder.setTextColor(Color.BLACK);
|
||||
tvInputReminder.setText(String.format("%02d:%02d", modified_habit.reminder_hour,
|
||||
modified_habit.reminder_min));
|
||||
}
|
||||
else
|
||||
{
|
||||
tvInputReminder.setTextColor(Color.GRAY);
|
||||
tvInputReminder.setText("Off");
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnSavedListener(OnSavedListener onSavedListener)
|
||||
{
|
||||
this.onSavedListener = onSavedListener;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Callback *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
int id = v.getId();
|
||||
|
||||
/* Due date spinner */
|
||||
if(id == R.id.input_reminder_time)
|
||||
{
|
||||
int default_hour = 8;
|
||||
int default_min = 0;
|
||||
|
||||
if(modified_habit.reminder_hour != null) {
|
||||
default_hour = modified_habit.reminder_hour;
|
||||
default_min = modified_habit.reminder_min;
|
||||
}
|
||||
|
||||
TimePickerDialog timePicker = TimePickerDialog.newInstance(new OnTimeSetListener()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void onTimeSet(RadialPickerLayout view, int hour, int minute)
|
||||
{
|
||||
modified_habit.reminder_hour = hour;
|
||||
modified_habit.reminder_min = minute;
|
||||
updateReminder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeCleared(RadialPickerLayout view)
|
||||
{
|
||||
modified_habit.reminder_hour = null;
|
||||
modified_habit.reminder_min = null;
|
||||
updateReminder();
|
||||
}
|
||||
}, default_hour, default_min, true);
|
||||
timePicker.show(getFragmentManager(), "timePicker");
|
||||
}
|
||||
|
||||
/* Save button */
|
||||
if(id == R.id.buttonSave)
|
||||
{
|
||||
Command command = null;
|
||||
|
||||
modified_habit.name = tvName.getText().toString().trim();
|
||||
modified_habit.description = tvDescription.getText().toString().trim();
|
||||
modified_habit.freq_num = Integer.parseInt(tvFreqNum.getText().toString());
|
||||
modified_habit.freq_den = Integer.parseInt(tvFreqDen.getText().toString());
|
||||
|
||||
Boolean valid = true;
|
||||
|
||||
if(modified_habit.name.length() == 0)
|
||||
{
|
||||
tvName.setError("Name cannot be blank.");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if(modified_habit.freq_den <= 0)
|
||||
{
|
||||
tvFreqNum.setError("Number must be positive.");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if(modified_habit.freq_num > modified_habit.freq_den)
|
||||
{
|
||||
tvFreqNum.setError("You can have at most one repetition per day");
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if(!valid)
|
||||
return;
|
||||
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putInt("pref_default_habit_freq_num", modified_habit.freq_num);
|
||||
editor.putInt("pref_default_habit_freq_den", modified_habit.freq_den);
|
||||
editor.apply();
|
||||
|
||||
Habit savedHabit = null;
|
||||
|
||||
if(mode == EDIT_MODE)
|
||||
{
|
||||
command = originalHabit.new EditCommand(modified_habit);
|
||||
savedHabit = originalHabit;
|
||||
}
|
||||
|
||||
if(mode == CREATE_MODE)
|
||||
command = new Habit.CreateCommand(modified_habit);
|
||||
|
||||
if(onSavedListener != null)
|
||||
onSavedListener.onSaved(command, savedHabit);
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
/* Discard button */
|
||||
if(id == R.id.buttonDiscard)
|
||||
{
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,672 +0,0 @@
|
||||
/* Copyright (C) 2016 Alinson Santos Xavier
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Vibrator;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.Display;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.mobeta.android.dslv.DragSortController;
|
||||
import com.mobeta.android.dslv.DragSortListView;
|
||||
import com.mobeta.android.dslv.DragSortListView.DropListener;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.Command;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.helpers.DialogHelper.OnSavedListener;
|
||||
import org.isoron.helpers.ReplayableActivity;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.ReminderHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class ListHabitsFragment extends Fragment
|
||||
implements OnSavedListener, OnItemClickListener, OnLongClickListener, DropListener,
|
||||
OnClickListener
|
||||
{
|
||||
|
||||
public static final int INACTIVE_COLOR = Color.rgb(230, 230, 230);
|
||||
|
||||
public interface OnHabitClickListener
|
||||
{
|
||||
void onHabitClicked(Habit habit);
|
||||
}
|
||||
|
||||
ListHabitsAdapter adapter;
|
||||
DragSortListView listView;
|
||||
ReplayableActivity activity;
|
||||
TextView tvNameHeader;
|
||||
long lastLongClick = 0;
|
||||
private int tvNameWidth;
|
||||
private int button_count;
|
||||
private View llEmpty;
|
||||
private ProgressBar progressBar;
|
||||
|
||||
private OnHabitClickListener habitClickListener;
|
||||
private boolean short_toggle_enabled;
|
||||
|
||||
private HashMap<Long, Habit> habits;
|
||||
private HashMap<Integer, Habit> positionToHabit;
|
||||
private HashMap<Long, int[]> checkmarks;
|
||||
private HashMap<Long, Integer> scores;
|
||||
|
||||
private Long lastLoadedTimestamp = null;
|
||||
private AsyncTask<Void, Integer, Void> currentFetchTask = null;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
int width = (int) (dm.widthPixels / dm.density);
|
||||
button_count = (int) ((width - 160) / 42);
|
||||
tvNameWidth = (int) ((width - 30 - button_count * 42) * dm.density);
|
||||
|
||||
habits = new HashMap<>();
|
||||
positionToHabit = new HashMap<>();
|
||||
checkmarks = new HashMap<>();
|
||||
scores = new HashMap<>();
|
||||
|
||||
View view = inflater.inflate(R.layout.list_habits_fragment, container, false);
|
||||
tvNameHeader = (TextView) view.findViewById(R.id.tvNameHeader);
|
||||
|
||||
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
|
||||
adapter = new ListHabitsAdapter(getActivity());
|
||||
listView = (DragSortListView) view.findViewById(R.id.listView);
|
||||
listView.setAdapter(adapter);
|
||||
listView.setOnItemClickListener(this);
|
||||
registerForContextMenu(listView);
|
||||
listView.setDropListener(this);
|
||||
|
||||
DragSortController controller = new DragSortController(listView);
|
||||
controller.setDragHandleId(R.id.tvStar);
|
||||
controller.setRemoveEnabled(false);
|
||||
controller.setSortEnabled(true);
|
||||
controller.setDragInitMode(1);
|
||||
|
||||
listView.setFloatViewManager(controller);
|
||||
listView.setOnTouchListener(controller);
|
||||
listView.setDragEnabled(true);
|
||||
|
||||
Typeface fontawesome = Typeface.createFromAsset(getActivity().getAssets(),
|
||||
"fontawesome-webfont.ttf");
|
||||
((TextView) view.findViewById(R.id.tvStarEmpty)).setTypeface(fontawesome);
|
||||
llEmpty = view.findViewById(R.id.llEmpty);
|
||||
|
||||
updateEmptyMessage();
|
||||
setHasOptionsMenu(true);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Activity activity)
|
||||
{
|
||||
super.onAttach(activity);
|
||||
habitClickListener = (OnHabitClickListener) activity;
|
||||
this.activity = (ReplayableActivity) activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
if(lastLoadedTimestamp == null || lastLoadedTimestamp != DateHelper.getStartOfToday())
|
||||
{
|
||||
updateHeader();
|
||||
fetchAllHabits();
|
||||
updateEmptyMessage();
|
||||
}
|
||||
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
short_toggle_enabled = prefs.getBoolean("pref_short_toggle", false);
|
||||
}
|
||||
|
||||
private void updateHeader()
|
||||
{
|
||||
LayoutInflater inflater = activity.getLayoutInflater();
|
||||
View view = getView();
|
||||
|
||||
if (view == null) return;
|
||||
|
||||
GregorianCalendar day = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
day.setTimeInMillis(DateHelper.getStartOfDay(DateHelper.getLocalTime()));
|
||||
|
||||
LinearLayout llButtonsHeader = (LinearLayout) view.findViewById(R.id.llButtonsHeader);
|
||||
llButtonsHeader.removeAllViews();
|
||||
|
||||
for (int i = 0; i < button_count; i++)
|
||||
{
|
||||
View check = inflater.inflate(R.layout.list_habits_header_check, null);
|
||||
Button btCheck = (Button) check.findViewById(R.id.tvCheck);
|
||||
btCheck.setText(day.getDisplayName(GregorianCalendar.DAY_OF_WEEK,
|
||||
GregorianCalendar.SHORT, Locale.US) + "\n" +
|
||||
Integer.toString(day.get(GregorianCalendar.DAY_OF_MONTH)));
|
||||
llButtonsHeader.addView(check);
|
||||
|
||||
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchAllHabits()
|
||||
{
|
||||
if(currentFetchTask != null) currentFetchTask.cancel(true);
|
||||
|
||||
currentFetchTask = new AsyncTask<Void, Integer, Void>()
|
||||
{
|
||||
HashMap<Long, Habit> newHabits = Habit.getAll();
|
||||
HashMap<Integer, Habit> newPositionToHabit = new HashMap<>();
|
||||
HashMap<Long, int[]> newCheckmarks = new HashMap<>();
|
||||
HashMap<Long, Integer> newScores = new HashMap<>();
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params)
|
||||
{
|
||||
long dateTo = DateHelper.getStartOfDay(DateHelper.getLocalTime());
|
||||
long dateFrom = dateTo - (button_count - 1) * DateHelper.millisecondsInOneDay;
|
||||
int[] empty = new int[button_count];
|
||||
|
||||
for(Habit h : newHabits.values())
|
||||
{
|
||||
newScores.put(h.getId(), 0);
|
||||
newPositionToHabit.put(h.position, h);
|
||||
newCheckmarks.put(h.getId(), empty);
|
||||
}
|
||||
|
||||
int current = 0;
|
||||
for(int i = 0; i < newHabits.size(); i++)
|
||||
{
|
||||
if(isCancelled()) return null;
|
||||
|
||||
Habit h = newPositionToHabit.get(i);
|
||||
newScores.put(h.getId(), h.getScore());
|
||||
newCheckmarks.put(h.getId(), h.getCheckmarks(dateFrom, dateTo));
|
||||
|
||||
publishProgress(current++, newHabits.size());
|
||||
}
|
||||
|
||||
commit();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void commit()
|
||||
{
|
||||
habits = newHabits;
|
||||
positionToHabit = newPositionToHabit;
|
||||
checkmarks = newCheckmarks;
|
||||
scores = newScores;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
progressBar.setIndeterminate(false);
|
||||
progressBar.setProgress(0);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values)
|
||||
{
|
||||
progressBar.setMax(values[1]);
|
||||
progressBar.setProgress(values[0]);
|
||||
|
||||
if(lastLoadedTimestamp == null)
|
||||
{
|
||||
commit();
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if(isCancelled()) return;
|
||||
|
||||
adapter.notifyDataSetChanged();
|
||||
updateEmptyMessage();
|
||||
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
currentFetchTask = null;
|
||||
lastLoadedTimestamp = DateHelper.getStartOfToday();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
currentFetchTask.execute();
|
||||
}
|
||||
|
||||
private void fetchHabit(final Long id)
|
||||
{
|
||||
new AsyncTask<Void, Void, Void>()
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground(Void... params)
|
||||
{
|
||||
long dateTo = DateHelper.getStartOfDay(DateHelper.getLocalTime());
|
||||
long dateFrom = dateTo - (button_count - 1) * DateHelper.millisecondsInOneDay;
|
||||
|
||||
Habit h = Habit.get(id);
|
||||
habits.put(id, h);
|
||||
scores.put(id, h.getScore());
|
||||
checkmarks.put(id, h.getCheckmarks(dateFrom, dateTo));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
new Handler().postDelayed(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if(getStatus() == Status.RUNNING)
|
||||
{
|
||||
progressBar.setIndeterminate(true);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
progressBar.setVisibility(View.GONE);
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
|
||||
{
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.list_habits_options, menu);
|
||||
|
||||
MenuItem showArchivedItem = menu.findItem(R.id.action_show_archived);
|
||||
showArchivedItem.setChecked(Habit.isIncludeArchived());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo)
|
||||
{
|
||||
super.onCreateContextMenu(menu, view, menuInfo);
|
||||
getActivity().getMenuInflater().inflate(R.menu.list_habits_context, menu);
|
||||
|
||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
||||
final Habit habit = habits.get(info.id);
|
||||
|
||||
if(habit.isArchived())
|
||||
menu.findItem(R.id.action_archive_habit).setVisible(false);
|
||||
else
|
||||
menu.findItem(R.id.action_unarchive_habit).setVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch(item.getItemId())
|
||||
{
|
||||
case R.id.action_add:
|
||||
{
|
||||
EditHabitFragment frag = EditHabitFragment.createHabitFragment();
|
||||
frag.setOnSavedListener(this);
|
||||
frag.show(getFragmentManager(), "dialog");
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_show_archived:
|
||||
{
|
||||
Habit.setIncludeArchived(!Habit.isIncludeArchived());
|
||||
fetchAllHabits();
|
||||
activity.invalidateOptionsMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onContextItemSelected(MenuItem menuItem)
|
||||
{
|
||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuItem.getMenuInfo();
|
||||
final int id = menuItem.getItemId();
|
||||
final Habit habit = habits.get(info.id);
|
||||
|
||||
if (id == R.id.action_edit_habit)
|
||||
{
|
||||
EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(habit.getId());
|
||||
frag.setOnSavedListener(this);
|
||||
frag.show(getFragmentManager(), "dialog");
|
||||
return true;
|
||||
}
|
||||
else if (id == R.id.action_archive_habit)
|
||||
{
|
||||
Command c = habit.new ArchiveCommand();
|
||||
executeCommand(c, null);
|
||||
}
|
||||
else if (id == R.id.action_unarchive_habit)
|
||||
{
|
||||
Command c = habit.new UnarchiveCommand();
|
||||
executeCommand(c, null);
|
||||
}
|
||||
|
||||
return super.onContextItemSelected(menuItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
if (new Date().getTime() - lastLongClick < 1000) return;
|
||||
|
||||
Habit habit = positionToHabit.get(position);
|
||||
habitClickListener.onHabitClicked(habit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaved(Command command, Object savedObject)
|
||||
{
|
||||
Habit h = (Habit) savedObject;
|
||||
|
||||
if(h == null) activity.executeCommand(command, null);
|
||||
else activity.executeCommand(command, h.getId());
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
ReminderHelper.createReminderAlarms(activity);
|
||||
}
|
||||
|
||||
private void updateEmptyMessage()
|
||||
{
|
||||
if(lastLoadedTimestamp == null)
|
||||
llEmpty.setVisibility(View.GONE);
|
||||
else
|
||||
llEmpty.setVisibility(habits.size() > 0 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v)
|
||||
{
|
||||
switch(v.getId())
|
||||
{
|
||||
case R.id.tvCheck:
|
||||
{
|
||||
lastLongClick = new Date().getTime();
|
||||
if(!short_toggle_enabled)
|
||||
{
|
||||
toggleCheck(v);
|
||||
Vibrator vb = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
vb.vibrate(100);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void toggleCheck(View v)
|
||||
{
|
||||
Habit habit = habits.get((Long) v.getTag(R.string.habit_key));
|
||||
|
||||
int offset = (Integer) v.getTag(R.string.offset_key);
|
||||
long timestamp = DateHelper.getStartOfDay(
|
||||
DateHelper.getLocalTime() - offset * DateHelper.millisecondsInOneDay);
|
||||
|
||||
if(v.getTag(R.string.toggle_key).equals(2))
|
||||
updateCheck(habit.color, (TextView) v, 0);
|
||||
else
|
||||
updateCheck(habit.color, (TextView) v, 2);
|
||||
|
||||
executeCommand(habit.new ToggleRepetitionCommand(timestamp), habit.getId());
|
||||
}
|
||||
|
||||
private void executeCommand(Command c, Long refreshKey)
|
||||
{
|
||||
activity.executeCommand(c, refreshKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(int from, int to)
|
||||
{
|
||||
Habit fromHabit = positionToHabit.get(from);
|
||||
Habit toHabit = positionToHabit.get(to);
|
||||
positionToHabit.put(to, fromHabit);
|
||||
positionToHabit.put(from, toHabit);
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
Habit.reorder(from, to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
switch(v.getId())
|
||||
{
|
||||
case R.id.tvCheck:
|
||||
if(short_toggle_enabled)
|
||||
toggleCheck(v);
|
||||
else
|
||||
activity.showToast(R.string.long_press_to_toggle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
class ListHabitsAdapter extends BaseAdapter
|
||||
{
|
||||
private Context context;
|
||||
private LayoutInflater inflater;
|
||||
private Typeface fontawesome;
|
||||
|
||||
public ListHabitsAdapter(Context context)
|
||||
{
|
||||
this.context = context;
|
||||
|
||||
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
fontawesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount()
|
||||
{
|
||||
return habits.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position)
|
||||
{
|
||||
return positionToHabit.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position)
|
||||
{
|
||||
return ((Habit) getItem(position)).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent)
|
||||
{
|
||||
final Habit habit = positionToHabit.get(position);
|
||||
|
||||
if (view == null || (Long) view.getTag(R.id.KEY_TIMESTAMP) !=
|
||||
DateHelper.getStartOfToday())
|
||||
{
|
||||
view = inflater.inflate(R.layout.list_habits_item, null);
|
||||
((TextView) view.findViewById(R.id.tvStar)).setTypeface(fontawesome);
|
||||
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(tvNameWidth,
|
||||
LayoutParams.WRAP_CONTENT, 1);
|
||||
view.findViewById(R.id.tvName).setLayoutParams(params);
|
||||
|
||||
Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
|
||||
.getDefaultDisplay();
|
||||
Point size = new Point();
|
||||
display.getSize(size);
|
||||
|
||||
for (int i = 0; i < button_count; i++)
|
||||
{
|
||||
View check = inflater.inflate(R.layout.list_habits_item_check, null);
|
||||
TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
|
||||
btCheck.setTypeface(fontawesome);
|
||||
btCheck.setOnLongClickListener(ListHabitsFragment.this);
|
||||
btCheck.setOnClickListener(ListHabitsFragment.this);
|
||||
((LinearLayout) view.findViewById(R.id.llButtons)).addView(check);
|
||||
}
|
||||
|
||||
view.setTag(R.id.KEY_TIMESTAMP, DateHelper.getStartOfToday());
|
||||
}
|
||||
|
||||
TextView tvStar = (TextView) view.findViewById(R.id.tvStar);
|
||||
TextView tvName = (TextView) view.findViewById(R.id.tvName);
|
||||
|
||||
if (habit == null)
|
||||
{
|
||||
tvName.setText(null);
|
||||
return view;
|
||||
}
|
||||
|
||||
LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
|
||||
llInner.setTag(R.string.habit_key, habit.getId());
|
||||
|
||||
int activeColor = habit.color;
|
||||
|
||||
tvName.setText(habit.name);
|
||||
tvName.setTextColor(activeColor);
|
||||
|
||||
if(habit.isArchived())
|
||||
{
|
||||
activeColor = ColorHelper.palette[12];
|
||||
tvName.setTextColor(activeColor);
|
||||
|
||||
tvStar.setText(context.getString(R.string.fa_archive));
|
||||
tvStar.setTextColor(activeColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
int score = scores.get(habit.getId());
|
||||
|
||||
if (score < Habit.HALF_STAR_CUTOFF)
|
||||
{
|
||||
tvStar.setText(context.getString(R.string.fa_star_o));
|
||||
tvStar.setTextColor(INACTIVE_COLOR);
|
||||
}
|
||||
else if (score < Habit.FULL_STAR_CUTOFF)
|
||||
{
|
||||
tvStar.setText(context.getString(R.string.fa_star_half_o));
|
||||
tvStar.setTextColor(INACTIVE_COLOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
tvStar.setText(context.getString(R.string.fa_star));
|
||||
tvStar.setTextColor(activeColor);
|
||||
}
|
||||
}
|
||||
|
||||
LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
|
||||
int m = llButtons.getChildCount();
|
||||
|
||||
int isChecked[] = checkmarks.get(habit.getId());
|
||||
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
|
||||
TextView tvCheck = (TextView) llButtons.getChildAt(i);
|
||||
tvCheck.setTag(R.string.habit_key, habit.getId());
|
||||
tvCheck.setTag(R.string.offset_key, i);
|
||||
updateCheck(activeColor, tvCheck, isChecked[i]);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCheck(int activeColor, TextView tvCheck, int check)
|
||||
{
|
||||
switch (check)
|
||||
{
|
||||
case 2:
|
||||
tvCheck.setText(R.string.fa_check);
|
||||
tvCheck.setTextColor(activeColor);
|
||||
tvCheck.setTag(R.string.toggle_key, 2);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
tvCheck.setText(R.string.fa_check);
|
||||
tvCheck.setTextColor(INACTIVE_COLOR);
|
||||
tvCheck.setTag(R.string.toggle_key, 1);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
tvCheck.setText(R.string.fa_times);
|
||||
tvCheck.setTextColor(INACTIVE_COLOR);
|
||||
tvCheck.setTag(R.string.toggle_key, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void onPostExecuteCommand(Long refreshKey)
|
||||
{
|
||||
if(refreshKey == null) fetchAllHabits();
|
||||
else fetchHabit(refreshKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.isoron.uhabits.dialogs;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
|
||||
public class WeekdayPickerDialog extends DialogFragment
|
||||
implements DialogInterface.OnMultiChoiceClickListener, DialogInterface.OnClickListener
|
||||
{
|
||||
|
||||
public interface OnWeekdaysPickedListener
|
||||
{
|
||||
void onWeekdaysPicked(boolean[] selectedDays);
|
||||
}
|
||||
|
||||
private boolean[] selectedDays;
|
||||
private OnWeekdaysPickedListener listener;
|
||||
|
||||
public void setListener(OnWeekdaysPickedListener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setSelectedDays(boolean[] selectedDays)
|
||||
{
|
||||
this.selectedDays = selectedDays;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState)
|
||||
{
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
|
||||
builder.setTitle("Select weekdays")
|
||||
.setMultiChoiceItems(DateHelper.getLongDayNames(), selectedDays, this)
|
||||
.setPositiveButton(android.R.string.yes, this)
|
||||
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which, boolean isChecked)
|
||||
{
|
||||
selectedDays[which] = isChecked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
if(listener != null) listener.onWeekdaysPicked(selectedDays);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
/* Copyright (C) 2016 Alinson Santos Xavier
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.fragments;
|
||||
|
||||
import android.app.DialogFragment;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.colorpicker.ColorPickerDialog;
|
||||
import com.android.colorpicker.ColorPickerSwatch;
|
||||
import com.android.datetimepicker.time.RadialPickerLayout;
|
||||
import com.android.datetimepicker.time.TimePickerDialog;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.Command;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.helpers.DialogHelper.OnSavedListener;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.dialogs.WeekdayPickerDialog;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
public class EditHabitFragment extends DialogFragment
|
||||
implements OnClickListener, WeekdayPickerDialog.OnWeekdaysPickedListener,
|
||||
TimePickerDialog.OnTimeSetListener
|
||||
{
|
||||
private Integer mode;
|
||||
static final int EDIT_MODE = 0;
|
||||
static final int CREATE_MODE = 1;
|
||||
|
||||
private OnSavedListener onSavedListener;
|
||||
|
||||
private Habit originalHabit, modifiedHabit;
|
||||
private TextView tvName, tvDescription, tvFreqNum, tvFreqDen, tvReminderTime, tvReminderDays;
|
||||
|
||||
private SharedPreferences prefs;
|
||||
private boolean is24HourMode;
|
||||
|
||||
static EditHabitFragment editSingleHabitFragment(long id)
|
||||
{
|
||||
EditHabitFragment frag = new EditHabitFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("habitId", id);
|
||||
args.putInt("editMode", EDIT_MODE);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
static EditHabitFragment createHabitFragment()
|
||||
{
|
||||
EditHabitFragment frag = new EditHabitFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putInt("editMode", CREATE_MODE);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
View view = inflater.inflate(R.layout.edit_habit, container, false);
|
||||
tvName = (TextView) view.findViewById(R.id.input_name);
|
||||
tvDescription = (TextView) view.findViewById(R.id.input_description);
|
||||
tvFreqNum = (TextView) view.findViewById(R.id.input_freq_num);
|
||||
tvFreqDen = (TextView) view.findViewById(R.id.input_freq_den);
|
||||
tvReminderTime = (TextView) view.findViewById(R.id.inputReminderTime);
|
||||
tvReminderDays = (TextView) view.findViewById(R.id.inputReminderDays);
|
||||
|
||||
Button buttonSave = (Button) view.findViewById(R.id.buttonSave);
|
||||
Button buttonDiscard = (Button) view.findViewById(R.id.buttonDiscard);
|
||||
|
||||
buttonSave.setOnClickListener(this);
|
||||
buttonDiscard.setOnClickListener(this);
|
||||
tvReminderTime.setOnClickListener(this);
|
||||
tvReminderDays.setOnClickListener(this);
|
||||
|
||||
ImageButton buttonPickColor = (ImageButton) view.findViewById(R.id.buttonPickColor);
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
|
||||
Bundle args = getArguments();
|
||||
mode = (Integer) args.get("editMode");
|
||||
|
||||
is24HourMode = DateFormat.is24HourFormat(getActivity());
|
||||
|
||||
if (mode == CREATE_MODE)
|
||||
{
|
||||
getDialog().setTitle(R.string.create_habit);
|
||||
modifiedHabit = new Habit();
|
||||
|
||||
int defaultNum = prefs.getInt("pref_default_habit_freq_num", modifiedHabit.freqNum);
|
||||
int defaultDen = prefs.getInt("pref_default_habit_freq_den", modifiedHabit.freqDen);
|
||||
int defaultColor = prefs.getInt("pref_default_habit_color", modifiedHabit.color);
|
||||
|
||||
modifiedHabit.color = defaultColor;
|
||||
modifiedHabit.freqNum = defaultNum;
|
||||
modifiedHabit.freqDen = defaultDen;
|
||||
}
|
||||
else if (mode == EDIT_MODE)
|
||||
{
|
||||
originalHabit = Habit.get((Long) args.get("habitId"));
|
||||
modifiedHabit = new Habit(originalHabit);
|
||||
|
||||
getDialog().setTitle(R.string.edit_habit);
|
||||
tvName.append(modifiedHabit.name);
|
||||
tvDescription.append(modifiedHabit.description);
|
||||
}
|
||||
|
||||
tvFreqNum.append(modifiedHabit.freqNum.toString());
|
||||
tvFreqDen.append(modifiedHabit.freqDen.toString());
|
||||
|
||||
changeColor(modifiedHabit.color);
|
||||
updateReminder();
|
||||
|
||||
buttonPickColor.setOnClickListener(this);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void changeColor(Integer color)
|
||||
{
|
||||
modifiedHabit.color = color;
|
||||
tvName.setTextColor(color);
|
||||
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putInt("pref_default_habit_color", color);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void updateReminder()
|
||||
{
|
||||
if (modifiedHabit.reminderHour != null)
|
||||
{
|
||||
tvReminderTime.setTextColor(Color.BLACK);
|
||||
tvReminderTime.setText(DateHelper.formatTime(getActivity(), modifiedHabit.reminderHour,
|
||||
modifiedHabit.reminderMin));
|
||||
tvReminderDays.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
tvReminderTime.setTextColor(Color.GRAY);
|
||||
tvReminderTime.setText(R.string.reminder_off);
|
||||
tvReminderDays.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
boolean weekdays[] = DateHelper.unpackWeekdayList(modifiedHabit.reminderDays);
|
||||
tvReminderDays.setText(DateHelper.formatWeekdayList(getActivity(), weekdays));
|
||||
}
|
||||
|
||||
public void setOnSavedListener(OnSavedListener onSavedListener)
|
||||
{
|
||||
this.onSavedListener = onSavedListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
switch(v.getId())
|
||||
{
|
||||
case R.id.inputReminderTime:
|
||||
onDateSpinnerClick();
|
||||
break;
|
||||
|
||||
case R.id.inputReminderDays:
|
||||
onWeekdayClick();
|
||||
break;
|
||||
|
||||
case R.id.buttonSave:
|
||||
onSaveButtonClick();
|
||||
break;
|
||||
|
||||
case R.id.buttonDiscard:
|
||||
dismiss();
|
||||
break;
|
||||
|
||||
case R.id.buttonPickColor:
|
||||
onColorButtonClick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onColorButtonClick()
|
||||
{
|
||||
ColorPickerDialog picker = ColorPickerDialog.newInstance(
|
||||
R.string.color_picker_default_title, ColorHelper.palette, modifiedHabit.color, 4,
|
||||
ColorPickerDialog.SIZE_SMALL);
|
||||
|
||||
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
|
||||
{
|
||||
public void onColorSelected(int color)
|
||||
{
|
||||
changeColor(color);
|
||||
}
|
||||
});
|
||||
picker.show(getFragmentManager(), "picker");
|
||||
}
|
||||
|
||||
private void onSaveButtonClick()
|
||||
{
|
||||
Command command = null;
|
||||
|
||||
modifiedHabit.name = tvName.getText().toString().trim();
|
||||
modifiedHabit.description = tvDescription.getText().toString().trim();
|
||||
modifiedHabit.freqNum = Integer.parseInt(tvFreqNum.getText().toString());
|
||||
modifiedHabit.freqDen = Integer.parseInt(tvFreqDen.getText().toString());
|
||||
|
||||
if (!validate()) return;
|
||||
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putInt("pref_default_habit_freq_num", modifiedHabit.freqNum);
|
||||
editor.putInt("pref_default_habit_freq_den", modifiedHabit.freqDen);
|
||||
editor.apply();
|
||||
|
||||
Habit savedHabit = null;
|
||||
|
||||
if (mode == EDIT_MODE)
|
||||
{
|
||||
command = originalHabit.new EditCommand(modifiedHabit);
|
||||
savedHabit = originalHabit;
|
||||
}
|
||||
|
||||
if (mode == CREATE_MODE) command = new Habit.CreateCommand(modifiedHabit);
|
||||
|
||||
if (onSavedListener != null) onSavedListener.onSaved(command, savedHabit);
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
private boolean validate()
|
||||
{
|
||||
Boolean valid = true;
|
||||
|
||||
if (modifiedHabit.name.length() == 0)
|
||||
{
|
||||
tvName.setError(getString(R.string.validation_name_should_not_be_blank));
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (modifiedHabit.freqNum <= 0)
|
||||
{
|
||||
tvFreqNum.setError(getString(R.string.validation_number_should_be_positive));
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (modifiedHabit.freqNum > modifiedHabit.freqDen)
|
||||
{
|
||||
tvFreqNum.setError(getString(R.string.validation_at_most_one_rep_per_day));
|
||||
valid = false;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
private void onDateSpinnerClick()
|
||||
{
|
||||
int defaultHour = 8;
|
||||
int defaultMin = 0;
|
||||
|
||||
if (modifiedHabit.reminderHour != null)
|
||||
{
|
||||
defaultHour = modifiedHabit.reminderHour;
|
||||
defaultMin = modifiedHabit.reminderMin;
|
||||
}
|
||||
|
||||
TimePickerDialog timePicker =
|
||||
TimePickerDialog.newInstance(this, defaultHour, defaultMin, is24HourMode);
|
||||
timePicker.show(getFragmentManager(), "timePicker");
|
||||
}
|
||||
|
||||
private void onWeekdayClick()
|
||||
{
|
||||
WeekdayPickerDialog dialog = new WeekdayPickerDialog();
|
||||
dialog.setListener(this);
|
||||
dialog.setSelectedDays(DateHelper.unpackWeekdayList(modifiedHabit.reminderDays));
|
||||
dialog.show(getFragmentManager(), "weekdayPicker");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeSet(RadialPickerLayout view, int hour, int minute)
|
||||
{
|
||||
modifiedHabit.reminderHour = hour;
|
||||
modifiedHabit.reminderMin = minute;
|
||||
updateReminder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTimeCleared(RadialPickerLayout view)
|
||||
{
|
||||
modifiedHabit.reminderHour = null;
|
||||
modifiedHabit.reminderMin = null;
|
||||
updateReminder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWeekdaysPicked(boolean[] selectedDays)
|
||||
{
|
||||
modifiedHabit.reminderDays = DateHelper.packWeekdayList(selectedDays);
|
||||
updateReminder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,785 @@
|
||||
/* Copyright (C) 2016 Alinson Santos Xavier
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.fragments;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.ActionMode;
|
||||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.View.OnLongClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.LinearLayout.LayoutParams;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.colorpicker.ColorPickerDialog;
|
||||
import com.android.colorpicker.ColorPickerSwatch;
|
||||
import com.mobeta.android.dslv.DragSortController;
|
||||
import com.mobeta.android.dslv.DragSortListView;
|
||||
import com.mobeta.android.dslv.DragSortListView.DropListener;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.Command;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.helpers.DialogHelper.OnSavedListener;
|
||||
import org.isoron.helpers.ReplayableActivity;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
||||
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
||||
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
||||
import org.isoron.uhabits.commands.UnarchiveHabitsCommand;
|
||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||
import org.isoron.uhabits.loaders.HabitListLoader;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ListHabitsFragment extends Fragment
|
||||
implements OnSavedListener, OnItemClickListener, OnLongClickListener, DropListener,
|
||||
OnClickListener, HabitListLoader.Listener, AdapterView.OnItemLongClickListener
|
||||
{
|
||||
private class ListHabitsActionBarCallback implements ActionMode.Callback
|
||||
{
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu)
|
||||
{
|
||||
getActivity().getMenuInflater().inflate(R.menu.list_habits_context, menu);
|
||||
updateTitle(mode);
|
||||
updateActions(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu)
|
||||
{
|
||||
updateTitle(mode);
|
||||
updateActions(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateActions(Menu menu)
|
||||
{
|
||||
boolean showEdit = (selectedPositions.size() == 1);
|
||||
boolean showColor = true;
|
||||
boolean showArchive = true;
|
||||
boolean showUnarchive = true;
|
||||
|
||||
if(showEdit) showColor = false;
|
||||
for(int i : selectedPositions)
|
||||
{
|
||||
Habit h = loader.habitsList.get(i);
|
||||
if(h.isArchived())
|
||||
{
|
||||
showColor = false;
|
||||
showArchive = false;
|
||||
}
|
||||
else showUnarchive = false;
|
||||
}
|
||||
|
||||
MenuItem itemEdit = menu.findItem(R.id.action_edit_habit);
|
||||
MenuItem itemColor = menu.findItem(R.id.action_color);
|
||||
MenuItem itemArchive = menu.findItem(R.id.action_archive_habit);
|
||||
MenuItem itemUnarchive = menu.findItem(R.id.action_unarchive_habit);
|
||||
|
||||
itemEdit.setVisible(showEdit);
|
||||
itemColor.setVisible(showColor);
|
||||
itemArchive.setVisible(showArchive);
|
||||
itemUnarchive.setVisible(showUnarchive);
|
||||
}
|
||||
|
||||
private void updateTitle(ActionMode mode)
|
||||
{
|
||||
mode.setTitle("" + selectedPositions.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(final ActionMode mode, MenuItem item)
|
||||
{
|
||||
final LinkedList<Habit> selectedHabits = new LinkedList<>();
|
||||
for(int i : selectedPositions)
|
||||
selectedHabits.add(loader.habitsList.get(i));
|
||||
|
||||
Habit firstHabit = selectedHabits.getFirst();
|
||||
|
||||
switch(item.getItemId())
|
||||
{
|
||||
case R.id.action_archive_habit:
|
||||
executeCommand(new ArchiveHabitsCommand(selectedHabits), null);
|
||||
mode.finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_unarchive_habit:
|
||||
executeCommand(new UnarchiveHabitsCommand(selectedHabits), null);
|
||||
mode.finish();
|
||||
return true;
|
||||
|
||||
case R.id.action_edit_habit:
|
||||
{
|
||||
EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(firstHabit.getId());
|
||||
frag.setOnSavedListener(ListHabitsFragment.this);
|
||||
frag.show(getFragmentManager(), "dialog");
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_color:
|
||||
{
|
||||
ColorPickerDialog picker = ColorPickerDialog.newInstance(
|
||||
R.string.color_picker_default_title, ColorHelper.palette,
|
||||
firstHabit.color, 4, ColorPickerDialog.SIZE_SMALL);
|
||||
|
||||
picker.setOnColorSelectedListener(new ColorPickerSwatch.OnColorSelectedListener()
|
||||
{
|
||||
public void onColorSelected(int color)
|
||||
{
|
||||
executeCommand(new ChangeHabitColorCommand(selectedHabits, color), null);
|
||||
mode.finish();
|
||||
}
|
||||
});
|
||||
picker.show(getFragmentManager(), "picker");
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_delete:
|
||||
{
|
||||
new AlertDialog.Builder(activity)
|
||||
.setTitle(R.string.delete_habits)
|
||||
.setMessage(R.string.delete_habits_message)
|
||||
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener()
|
||||
{
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which)
|
||||
{
|
||||
executeCommand(new DeleteHabitsCommand(selectedHabits), null);
|
||||
mode.finish();
|
||||
}
|
||||
}).setNegativeButton(android.R.string.no, null)
|
||||
.show();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode)
|
||||
{
|
||||
actionMode = null;
|
||||
|
||||
selectedPositions.clear();
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
listView.setDragEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
public static final int INACTIVE_COLOR = Color.rgb(200, 200, 200);
|
||||
public static final int INACTIVE_CHECKMARK_COLOR = Color.rgb(230, 230, 230);
|
||||
|
||||
public static final int HINT_INTERVAL = 5;
|
||||
public static final int HINT_INTERVAL_OFFSET = 2;
|
||||
|
||||
public interface OnHabitClickListener
|
||||
{
|
||||
void onHabitClicked(Habit habit);
|
||||
}
|
||||
|
||||
ListHabitsAdapter adapter;
|
||||
DragSortListView listView;
|
||||
ReplayableActivity activity;
|
||||
TextView tvNameHeader;
|
||||
long lastLongClick = 0;
|
||||
|
||||
private int tvNameWidth;
|
||||
private int buttonCount;
|
||||
private View llEmpty;
|
||||
private View llHint;
|
||||
|
||||
private OnHabitClickListener habitClickListener;
|
||||
private boolean isShortToggleEnabled;
|
||||
|
||||
private HabitListLoader loader;
|
||||
private boolean showArchived;
|
||||
private SharedPreferences prefs;
|
||||
|
||||
private ActionMode actionMode;
|
||||
private List<Integer> selectedPositions;
|
||||
private DragSortController dragSortController;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
int width = (int) (dm.widthPixels / dm.density);
|
||||
buttonCount = (int) ((width - 160) / 42.0);
|
||||
tvNameWidth = (int) ((width - 30 - buttonCount * 42) * dm.density);
|
||||
|
||||
loader = new HabitListLoader();
|
||||
loader.setListener(this);
|
||||
loader.setCheckmarkCount(buttonCount);
|
||||
|
||||
View view = inflater.inflate(R.layout.list_habits_fragment, container, false);
|
||||
tvNameHeader = (TextView) view.findViewById(R.id.tvNameHeader);
|
||||
|
||||
ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
|
||||
loader.setProgressBar(progressBar);
|
||||
|
||||
adapter = new ListHabitsAdapter(getActivity());
|
||||
listView = (DragSortListView) view.findViewById(R.id.listView);
|
||||
listView.setAdapter(adapter);
|
||||
listView.setOnItemClickListener(this);
|
||||
listView.setOnItemLongClickListener(this);
|
||||
listView.setDropListener(this);
|
||||
listView.setDragListener(new DragSortListView.DragListener()
|
||||
{
|
||||
@Override
|
||||
public void drag(int from, int to)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startDrag(int position)
|
||||
{
|
||||
selectItem(position);
|
||||
}
|
||||
});
|
||||
|
||||
dragSortController = new DragSortController(listView) {
|
||||
@Override
|
||||
public View onCreateFloatView(int position)
|
||||
{
|
||||
return adapter.getView(position, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyFloatView(View floatView)
|
||||
{
|
||||
}
|
||||
};
|
||||
dragSortController.setRemoveEnabled(false);
|
||||
|
||||
listView.setFloatViewManager(dragSortController);
|
||||
listView.setDragEnabled(true);
|
||||
listView.setLongClickable(true);
|
||||
|
||||
llHint = view.findViewById(R.id.llHint);
|
||||
llHint.setOnClickListener(this);
|
||||
|
||||
Typeface fontawesome = Typeface.createFromAsset(getActivity().getAssets(),
|
||||
"fontawesome-webfont.ttf");
|
||||
((TextView) view.findViewById(R.id.tvStarEmpty)).setTypeface(fontawesome);
|
||||
llEmpty = view.findViewById(R.id.llEmpty);
|
||||
|
||||
loader.updateAllHabits(true);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
selectedPositions = new LinkedList<>();
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public void onAttach(Activity activity)
|
||||
{
|
||||
super.onAttach(activity);
|
||||
this.activity = (ReplayableActivity) activity;
|
||||
|
||||
habitClickListener = (OnHabitClickListener) activity;
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
super.onResume();
|
||||
Long timestamp = loader.getLastLoadTimestamp();
|
||||
|
||||
if (timestamp != null && timestamp != DateHelper.getStartOfToday())
|
||||
loader.updateAllHabits(true);
|
||||
|
||||
updateEmptyMessage();
|
||||
updateHeader();
|
||||
showNextHint();
|
||||
|
||||
adapter.notifyDataSetChanged();
|
||||
isShortToggleEnabled = prefs.getBoolean("pref_short_toggle", false);
|
||||
}
|
||||
|
||||
private void updateHeader()
|
||||
{
|
||||
LayoutInflater inflater = activity.getLayoutInflater();
|
||||
View view = getView();
|
||||
|
||||
if (view == null) return;
|
||||
|
||||
GregorianCalendar day = DateHelper.getStartOfTodayCalendar();
|
||||
|
||||
LinearLayout llButtonsHeader = (LinearLayout) view.findViewById(R.id.llButtonsHeader);
|
||||
llButtonsHeader.removeAllViews();
|
||||
|
||||
for (int i = 0; i < buttonCount; i++)
|
||||
{
|
||||
View tvDay = inflater.inflate(R.layout.list_habits_header_check, null);
|
||||
Button btCheck = (Button) tvDay.findViewById(R.id.tvCheck);
|
||||
btCheck.setText(DateHelper.formatHeaderDate(day));
|
||||
llButtonsHeader.addView(tvDay);
|
||||
|
||||
day.add(GregorianCalendar.DAY_OF_MONTH, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished()
|
||||
{
|
||||
adapter.notifyDataSetChanged();
|
||||
updateEmptyMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
|
||||
{
|
||||
super.onCreateOptionsMenu(menu, inflater);
|
||||
inflater.inflate(R.menu.list_habits_options, menu);
|
||||
|
||||
MenuItem showArchivedItem = menu.findItem(R.id.action_show_archived);
|
||||
showArchivedItem.setChecked(showArchived);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo)
|
||||
{
|
||||
super.onCreateContextMenu(menu, view, menuInfo);
|
||||
getActivity().getMenuInflater().inflate(R.menu.list_habits_context, menu);
|
||||
|
||||
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
|
||||
final Habit habit = loader.habits.get(info.id);
|
||||
|
||||
if (habit.isArchived()) menu.findItem(R.id.action_archive_habit).setVisible(false);
|
||||
else menu.findItem(R.id.action_unarchive_habit).setVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item)
|
||||
{
|
||||
switch (item.getItemId())
|
||||
{
|
||||
case R.id.action_add:
|
||||
{
|
||||
EditHabitFragment frag = EditHabitFragment.createHabitFragment();
|
||||
frag.setOnSavedListener(this);
|
||||
frag.show(getFragmentManager(), "dialog");
|
||||
return true;
|
||||
}
|
||||
|
||||
case R.id.action_show_archived:
|
||||
{
|
||||
showArchived = !showArchived;
|
||||
loader.setIncludeArchived(showArchived);
|
||||
loader.updateAllHabits(true);
|
||||
activity.invalidateOptionsMenu();
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView parent, View view, int position, long id)
|
||||
{
|
||||
if (new Date().getTime() - lastLongClick < 1000) return;
|
||||
|
||||
if(actionMode == null)
|
||||
{
|
||||
Habit habit = loader.habitsList.get(position);
|
||||
habitClickListener.onHabitClicked(habit);
|
||||
}
|
||||
else
|
||||
{
|
||||
int k = selectedPositions.indexOf(position);
|
||||
if(k < 0)
|
||||
selectedPositions.add(position);
|
||||
else
|
||||
selectedPositions.remove(k);
|
||||
|
||||
if(selectedPositions.isEmpty()) actionMode.finish();
|
||||
else actionMode.invalidate();
|
||||
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id)
|
||||
{
|
||||
selectItem(position);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void selectItem(int position)
|
||||
{
|
||||
if(!selectedPositions.contains(position))
|
||||
selectedPositions.add(position);
|
||||
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
if(actionMode == null)
|
||||
{
|
||||
actionMode = getActivity().startActionMode(new ListHabitsActionBarCallback());
|
||||
// listView.setDragEnabled(false);
|
||||
}
|
||||
|
||||
if(actionMode != null) actionMode.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaved(Command command, Object savedObject)
|
||||
{
|
||||
Habit h = (Habit) savedObject;
|
||||
|
||||
if (h == null) activity.executeCommand(command, null);
|
||||
else activity.executeCommand(command, h.getId());
|
||||
adapter.notifyDataSetChanged();
|
||||
|
||||
ReminderHelper.createReminderAlarms(activity);
|
||||
|
||||
if(actionMode != null) actionMode.finish();
|
||||
}
|
||||
|
||||
private void updateEmptyMessage()
|
||||
{
|
||||
if (loader.getLastLoadTimestamp() == null) llEmpty.setVisibility(View.GONE);
|
||||
else llEmpty.setVisibility(loader.habits.size() > 0 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v)
|
||||
{
|
||||
lastLongClick = new Date().getTime();
|
||||
|
||||
switch (v.getId())
|
||||
{
|
||||
case R.id.tvCheck:
|
||||
onCheckmarkLongClick(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void onCheckmarkLongClick(View v)
|
||||
{
|
||||
if (isShortToggleEnabled) return;
|
||||
|
||||
toggleCheck(v);
|
||||
DialogHelper.vibrate(activity, 100);
|
||||
}
|
||||
|
||||
private void toggleCheck(View v)
|
||||
{
|
||||
Long tag = (Long) v.getTag(R.string.habit_key);
|
||||
Habit habit = loader.habits.get(tag);
|
||||
|
||||
int offset = (Integer) v.getTag(R.string.offset_key);
|
||||
long timestamp = DateHelper.getStartOfDay(
|
||||
DateHelper.getLocalTime() - offset * DateHelper.millisecondsInOneDay);
|
||||
|
||||
if (v.getTag(R.string.toggle_key).equals(2)) updateCheckmark(habit.color, (TextView) v, 0);
|
||||
else updateCheckmark(habit.color, (TextView) v, 2);
|
||||
|
||||
executeCommand(habit.new ToggleRepetitionCommand(timestamp), habit.getId());
|
||||
}
|
||||
|
||||
private void executeCommand(Command c, Long refreshKey)
|
||||
{
|
||||
activity.executeCommand(c, refreshKey);
|
||||
}
|
||||
|
||||
private void hideHint()
|
||||
{
|
||||
llHint.animate().alpha(0f).setDuration(500).setListener(new AnimatorListenerAdapter()
|
||||
{
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation)
|
||||
{
|
||||
llHint.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void showNextHint()
|
||||
{
|
||||
Integer lastHintNumber = prefs.getInt("last_hint_number", -1);
|
||||
Long lastHintTimestamp = prefs.getLong("last_hint_timestamp", -1);
|
||||
|
||||
if(DateHelper.getStartOfToday() > lastHintTimestamp)
|
||||
showHint(lastHintNumber + 1);
|
||||
}
|
||||
|
||||
private void showHint(int hintNumber)
|
||||
{
|
||||
String[] hints = activity.getResources().getStringArray(R.array.hints);
|
||||
if(hintNumber >= hints.length) return;
|
||||
|
||||
prefs.edit().putInt("last_hint_number", hintNumber).apply();
|
||||
prefs.edit().putLong("last_hint_timestamp", DateHelper.getStartOfToday()).apply();
|
||||
|
||||
TextView tvContent = (TextView) llHint.findViewById(R.id.hintContent);
|
||||
tvContent.setText(hints[hintNumber]);
|
||||
|
||||
llHint.setAlpha(0.0f);
|
||||
llHint.setVisibility(View.VISIBLE);
|
||||
llHint.animate().alpha(1f).setDuration(500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drop(int from, int to)
|
||||
{
|
||||
if(from == to) return;
|
||||
if(actionMode != null) actionMode.finish();
|
||||
|
||||
loader.reorder(from, to);
|
||||
adapter.notifyDataSetChanged();
|
||||
loader.updateAllHabits(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v)
|
||||
{
|
||||
switch (v.getId())
|
||||
{
|
||||
case R.id.tvCheck:
|
||||
if (isShortToggleEnabled) toggleCheck(v);
|
||||
else activity.showToast(R.string.long_press_to_toggle);
|
||||
break;
|
||||
|
||||
case R.id.llHint:
|
||||
hideHint();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class ListHabitsAdapter extends BaseAdapter
|
||||
{
|
||||
private LayoutInflater inflater;
|
||||
private Typeface fontawesome;
|
||||
|
||||
public ListHabitsAdapter(Context context)
|
||||
{
|
||||
|
||||
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
fontawesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount()
|
||||
{
|
||||
return loader.habits.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position)
|
||||
{
|
||||
return loader.habitsList.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position)
|
||||
{
|
||||
return ((Habit) getItem(position)).getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View view, ViewGroup parent)
|
||||
{
|
||||
final Habit habit = loader.habitsList.get(position);
|
||||
|
||||
if (view == null ||
|
||||
(Long) view.getTag(R.id.timestamp_key) != DateHelper.getStartOfToday())
|
||||
{
|
||||
view = inflater.inflate(R.layout.list_habits_item, null);
|
||||
((TextView) view.findViewById(R.id.tvStar)).setTypeface(fontawesome);
|
||||
|
||||
LinearLayout.LayoutParams params =
|
||||
new LinearLayout.LayoutParams(tvNameWidth, LayoutParams.WRAP_CONTENT, 1);
|
||||
view.findViewById(R.id.tvName).setLayoutParams(params);
|
||||
|
||||
inflateCheckmarkButtons(view);
|
||||
|
||||
view.setTag(R.id.timestamp_key, DateHelper.getStartOfToday());
|
||||
}
|
||||
|
||||
TextView tvStar = ((TextView) view.findViewById(R.id.tvStar));
|
||||
TextView tvName = (TextView) view.findViewById(R.id.tvName);
|
||||
LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
|
||||
LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
|
||||
|
||||
llInner.setTag(R.string.habit_key, habit.getId());
|
||||
|
||||
updateNameAndIcon(habit, tvStar, tvName);
|
||||
updateCheckmarkButtons(habit, llButtons);
|
||||
|
||||
boolean selected = selectedPositions.contains(position);
|
||||
if(selected)
|
||||
llInner.setBackgroundResource(R.drawable.selected_box);
|
||||
else
|
||||
{
|
||||
if (android.os.Build.VERSION.SDK_INT >= 21)
|
||||
llInner.setBackgroundResource(R.drawable.ripple_white);
|
||||
else
|
||||
llInner.setBackgroundColor(Color.WHITE);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void inflateCheckmarkButtons(View view)
|
||||
{
|
||||
for (int i = 0; i < buttonCount; i++)
|
||||
{
|
||||
View check = inflater.inflate(R.layout.list_habits_item_check, null);
|
||||
TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
|
||||
btCheck.setTypeface(fontawesome);
|
||||
btCheck.setOnLongClickListener(ListHabitsFragment.this);
|
||||
btCheck.setOnClickListener(ListHabitsFragment.this);
|
||||
((LinearLayout) view.findViewById(R.id.llButtons)).addView(check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCheckmarkButtons(Habit habit, LinearLayout llButtons)
|
||||
{
|
||||
int activeColor = getActiveColor(habit);
|
||||
int m = llButtons.getChildCount();
|
||||
Long habitId = habit.getId();
|
||||
|
||||
int isChecked[] = loader.checkmarks.get(habitId);
|
||||
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
|
||||
TextView tvCheck = (TextView) llButtons.getChildAt(i);
|
||||
tvCheck.setTag(R.string.habit_key, habitId);
|
||||
tvCheck.setTag(R.string.offset_key, i);
|
||||
updateCheckmark(activeColor, tvCheck, isChecked[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateNameAndIcon(Habit habit, TextView tvStar, TextView tvName)
|
||||
{
|
||||
int activeColor = getActiveColor(habit);
|
||||
|
||||
tvName.setText(habit.name);
|
||||
tvName.setTextColor(activeColor);
|
||||
|
||||
if (habit.isArchived())
|
||||
{
|
||||
tvStar.setText(getString(R.string.fa_archive));
|
||||
tvStar.setTextColor(activeColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
int score = loader.scores.get(habit.getId());
|
||||
|
||||
if (score < Habit.HALF_STAR_CUTOFF)
|
||||
{
|
||||
tvStar.setText(getString(R.string.fa_star_o));
|
||||
tvStar.setTextColor(INACTIVE_COLOR);
|
||||
}
|
||||
else if (score < Habit.FULL_STAR_CUTOFF)
|
||||
{
|
||||
tvStar.setText(getString(R.string.fa_star_half_o));
|
||||
tvStar.setTextColor(INACTIVE_COLOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
tvStar.setText(getString(R.string.fa_star));
|
||||
tvStar.setTextColor(activeColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getActiveColor(Habit habit)
|
||||
{
|
||||
int activeColor = habit.color;
|
||||
if(habit.isArchived()) activeColor = INACTIVE_COLOR;
|
||||
|
||||
return activeColor;
|
||||
}
|
||||
|
||||
private void updateCheckmark(int activeColor, TextView tvCheck, int check)
|
||||
{
|
||||
switch (check)
|
||||
{
|
||||
case 2:
|
||||
tvCheck.setText(R.string.fa_check);
|
||||
tvCheck.setTextColor(activeColor);
|
||||
tvCheck.setTag(R.string.toggle_key, 2);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
tvCheck.setText(R.string.fa_check);
|
||||
tvCheck.setTextColor(INACTIVE_CHECKMARK_COLOR);
|
||||
tvCheck.setTag(R.string.toggle_key, 1);
|
||||
break;
|
||||
|
||||
case 0:
|
||||
tvCheck.setText(R.string.fa_times);
|
||||
tvCheck.setTextColor(INACTIVE_CHECKMARK_COLOR);
|
||||
tvCheck.setTag(R.string.toggle_key, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void onPostExecuteCommand(Long refreshKey)
|
||||
{
|
||||
if (refreshKey == null) loader.updateAllHabits(true);
|
||||
else loader.updateHabit(refreshKey);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.dialogs;
|
||||
package org.isoron.uhabits.fragments;
|
||||
|
||||
import android.app.backup.BackupManager;
|
||||
import android.content.SharedPreferences;
|
||||
@@ -23,7 +23,8 @@ import android.preference.PreferenceFragment;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||
public class SettingsFragment extends PreferenceFragment
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||
{
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
@@ -14,12 +14,11 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits.dialogs;
|
||||
package org.isoron.uhabits.fragments;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
@@ -33,7 +32,7 @@ import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.Command;
|
||||
import org.isoron.helpers.DialogHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.ReminderHelper;
|
||||
import org.isoron.uhabits.helpers.ReminderHelper;
|
||||
import org.isoron.uhabits.ShowHabitActivity;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.views.HabitHistoryView;
|
||||
@@ -56,8 +55,6 @@ public class ShowHabitFragment extends Fragment implements DialogHelper.OnSavedL
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState)
|
||||
{
|
||||
Log.d("ShowHabitActivity", "Creating view...");
|
||||
|
||||
View view = inflater.inflate(R.layout.show_habit, container, false);
|
||||
activity = (ShowHabitActivity) getActivity();
|
||||
habit = activity.habit;
|
||||
@@ -80,9 +77,9 @@ public class ShowHabitFragment extends Fragment implements DialogHelper.OnSavedL
|
||||
tvStreaks.setTextColor(habit.color);
|
||||
|
||||
LinearLayout llOverview = (LinearLayout) view.findViewById(R.id.llOverview);
|
||||
llOverview.addView(new RingView(activity, (int) activity.getResources().getDimension(
|
||||
R.dimen.small_square_size) * 4, habit.color,
|
||||
((float) habit.getScore() / Habit.MAX_SCORE), "Habit strength"));
|
||||
llOverview.addView(new RingView(activity,
|
||||
(int) activity.getResources().getDimension(R.dimen.small_square_size) * 4,
|
||||
habit.color, ((float) habit.getScore() / Habit.MAX_SCORE), activity.getString(R.string.habit_strength)));
|
||||
|
||||
LinearLayout llStrength = (LinearLayout) view.findViewById(R.id.llStrength);
|
||||
llStrength.addView(new HabitScoreView(activity, habit,
|
||||
@@ -130,7 +127,7 @@ public class ShowHabitFragment extends Fragment implements DialogHelper.OnSavedL
|
||||
{
|
||||
Habit h = (Habit) savedObject;
|
||||
|
||||
if(h == null) activity.executeCommand(command, null);
|
||||
if (h == null) activity.executeCommand(command, null);
|
||||
else activity.executeCommand(command, h.getId());
|
||||
|
||||
ReminderHelper.createReminderAlarms(activity);
|
||||
@@ -14,7 +14,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.isoron.uhabits;
|
||||
package org.isoron.uhabits.helpers;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
@@ -24,6 +24,8 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.ReminderAlarmReceiver;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.text.DateFormat;
|
||||
@@ -40,43 +42,41 @@ public class ReminderHelper
|
||||
|
||||
public static void createReminderAlarm(Context context, Habit habit, Long reminderTime)
|
||||
{
|
||||
Uri uri = Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId());
|
||||
|
||||
if (reminderTime == null)
|
||||
{
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeInMillis(System.currentTimeMillis());
|
||||
calendar.set(Calendar.HOUR_OF_DAY, habit.reminder_hour);
|
||||
calendar.set(Calendar.MINUTE, habit.reminder_min);
|
||||
calendar.set(Calendar.HOUR_OF_DAY, habit.reminderHour);
|
||||
calendar.set(Calendar.MINUTE, habit.reminderMin);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
|
||||
reminderTime = calendar.getTimeInMillis();
|
||||
|
||||
if (System.currentTimeMillis() > reminderTime)
|
||||
{
|
||||
reminderTime += AlarmManager.INTERVAL_DAY;
|
||||
}
|
||||
}
|
||||
|
||||
long timestamp = DateHelper.getStartOfDay(DateHelper.toLocalTime(reminderTime));
|
||||
|
||||
Uri uri = Uri.parse(String.format("content://org.isoron.uhabits/habit/%d", habit.getId()));
|
||||
|
||||
Intent alarmIntent = new Intent(context, ReminderAlarmReceiver.class);
|
||||
alarmIntent.setAction(ReminderAlarmReceiver.ACTION_REMIND);
|
||||
alarmIntent.setData(uri);
|
||||
alarmIntent.putExtra("timestamp", timestamp);
|
||||
alarmIntent.putExtra("reminderTime", reminderTime);
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
|
||||
((int) (habit.getId() % Integer.MAX_VALUE)) + 1, alarmIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
PendingIntent pendingIntent =
|
||||
PendingIntent.getBroadcast(context, ((int) (habit.getId() % Integer.MAX_VALUE)) + 1,
|
||||
alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= 19)
|
||||
{
|
||||
manager.setExact(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent);
|
||||
}
|
||||
else
|
||||
{
|
||||
manager.set(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent);
|
||||
}
|
||||
|
||||
Log.d("Alarm", String.format("Setting alarm (%s): %s",
|
||||
Log.d("ReminderHelper", String.format("Setting alarm (%s): %s",
|
||||
DateFormat.getDateTimeInstance().format(new Date(reminderTime)), habit.name));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Alinson Santos Xavier
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.loaders;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class HabitListLoader
|
||||
{
|
||||
public interface Listener
|
||||
{
|
||||
void onLoadFinished();
|
||||
}
|
||||
|
||||
private AsyncTask<Void, Integer, Void> currentFetchTask;
|
||||
private int checkmarkCount;
|
||||
private ProgressBar progressBar;
|
||||
|
||||
private Listener listener;
|
||||
private Long lastLoadTimestamp;
|
||||
|
||||
public HashMap<Long, Habit> habits;
|
||||
public List<Habit> habitsList;
|
||||
public HashMap<Long, int[]> checkmarks;
|
||||
public HashMap<Long, Integer> scores;
|
||||
|
||||
boolean includeArchived;
|
||||
|
||||
public void setIncludeArchived(boolean includeArchived)
|
||||
{
|
||||
this.includeArchived = includeArchived;
|
||||
}
|
||||
|
||||
public void setProgressBar(ProgressBar progressBar)
|
||||
{
|
||||
this.progressBar = progressBar;
|
||||
}
|
||||
|
||||
public void setCheckmarkCount(int checkmarkCount)
|
||||
{
|
||||
this.checkmarkCount = checkmarkCount;
|
||||
}
|
||||
|
||||
public void setListener(Listener listener)
|
||||
{
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public Long getLastLoadTimestamp()
|
||||
{
|
||||
return lastLoadTimestamp;
|
||||
}
|
||||
|
||||
public HabitListLoader()
|
||||
{
|
||||
habits = new HashMap<>();
|
||||
checkmarks = new HashMap<>();
|
||||
scores = new HashMap<>();
|
||||
}
|
||||
|
||||
public void reorder(int from, int to)
|
||||
{
|
||||
Habit fromHabit = habitsList.get(from);
|
||||
Habit toHabit = habitsList.get(to);
|
||||
|
||||
habitsList.remove(from);
|
||||
habitsList.add(to, fromHabit);
|
||||
|
||||
Habit.reorder(fromHabit, toHabit);
|
||||
}
|
||||
|
||||
public void updateAllHabits(final boolean updateScoresAndCheckmarks)
|
||||
{
|
||||
if (currentFetchTask != null) currentFetchTask.cancel(true);
|
||||
|
||||
currentFetchTask = new AsyncTask<Void, Integer, Void>()
|
||||
{
|
||||
public HashMap<Long, Habit> newHabits;
|
||||
public HashMap<Long, int[]> newCheckmarks;
|
||||
public HashMap<Long, Integer> newScores;
|
||||
public List<Habit> newHabitList;
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params)
|
||||
{
|
||||
newHabits = new HashMap<>();
|
||||
newCheckmarks = new HashMap<>();
|
||||
newScores = new HashMap<>();
|
||||
newHabitList = Habit.getAll(includeArchived);
|
||||
|
||||
long dateTo = DateHelper.getStartOfDay(DateHelper.getLocalTime());
|
||||
long dateFrom = dateTo - (checkmarkCount - 1) * DateHelper.millisecondsInOneDay;
|
||||
int[] empty = new int[checkmarkCount];
|
||||
|
||||
for(Habit h : newHabitList)
|
||||
{
|
||||
Long id = h.getId();
|
||||
|
||||
newHabits.put(id, h);
|
||||
|
||||
if(checkmarks.containsKey(id))
|
||||
newCheckmarks.put(id, checkmarks.get(id));
|
||||
else
|
||||
newCheckmarks.put(id, empty);
|
||||
|
||||
if(scores.containsKey(id))
|
||||
newScores.put(id, scores.get(id));
|
||||
else
|
||||
newScores.put(id, 0);
|
||||
}
|
||||
|
||||
commit();
|
||||
|
||||
if(!updateScoresAndCheckmarks) return null;
|
||||
|
||||
int current = 0;
|
||||
for (Habit h : newHabitList)
|
||||
{
|
||||
if (isCancelled()) return null;
|
||||
|
||||
Long id = h.getId();
|
||||
newScores.put(id, h.getScore());
|
||||
newCheckmarks.put(id, h.getCheckmarks(dateFrom, dateTo));
|
||||
|
||||
publishProgress(current++, newHabits.size());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void commit()
|
||||
{
|
||||
habits = newHabits;
|
||||
scores = newScores;
|
||||
checkmarks = newCheckmarks;
|
||||
habitsList = newHabitList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
if(progressBar != null)
|
||||
{
|
||||
progressBar.setIndeterminate(false);
|
||||
progressBar.setProgress(0);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(Integer... values)
|
||||
{
|
||||
if(progressBar != null)
|
||||
{
|
||||
progressBar.setMax(values[1]);
|
||||
progressBar.setProgress(values[0]);
|
||||
}
|
||||
|
||||
if(listener != null) listener.onLoadFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if (isCancelled()) return;
|
||||
|
||||
if(progressBar != null) progressBar.setVisibility(View.INVISIBLE);
|
||||
lastLoadTimestamp = DateHelper.getStartOfToday();
|
||||
currentFetchTask = null;
|
||||
|
||||
if(listener != null) listener.onLoadFinished();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
currentFetchTask.execute();
|
||||
}
|
||||
|
||||
public void updateHabit(final Long id)
|
||||
{
|
||||
new AsyncTask<Void, Void, Void>()
|
||||
{
|
||||
@Override
|
||||
protected Void doInBackground(Void... params)
|
||||
{
|
||||
long dateTo = DateHelper.getStartOfDay(DateHelper.getLocalTime());
|
||||
long dateFrom = dateTo - (checkmarkCount - 1) * DateHelper.millisecondsInOneDay;
|
||||
|
||||
Habit h = Habit.get(id);
|
||||
habits.put(id, h);
|
||||
scores.put(id, h.getScore());
|
||||
checkmarks.put(id, h.getCheckmarks(dateFrom, dateTo));
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute()
|
||||
{
|
||||
new Handler().postDelayed(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (getStatus() == Status.RUNNING)
|
||||
{
|
||||
if(progressBar != null)
|
||||
{
|
||||
progressBar.setIndeterminate(true);
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid)
|
||||
{
|
||||
if(progressBar != null) progressBar.setVisibility(View.GONE);
|
||||
|
||||
if(listener != null)
|
||||
listener.onLoadFinished();
|
||||
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
}
|
||||
@@ -37,18 +37,15 @@ import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@Table(name = "Habits")
|
||||
public class Habit extends Model
|
||||
{
|
||||
|
||||
public static final int HALF_STAR_CUTOFF = 5999000;
|
||||
public static final int FULL_STAR_CUTOFF = 12973000;
|
||||
public static final int MAX_SCORE = 19259500;
|
||||
|
||||
private static boolean includeArchived = false;
|
||||
public static final int HALF_STAR_CUTOFF = 9629750;
|
||||
public static final int FULL_STAR_CUTOFF = 15407600;
|
||||
public static final int MAX_SCORE = 19259500;
|
||||
|
||||
@Column(name = "name")
|
||||
public String name;
|
||||
@@ -57,10 +54,10 @@ public class Habit extends Model
|
||||
public String description;
|
||||
|
||||
@Column(name = "freq_num")
|
||||
public Integer freq_num;
|
||||
public Integer freqNum;
|
||||
|
||||
@Column(name = "freq_den")
|
||||
public Integer freq_den;
|
||||
public Integer freqDen;
|
||||
|
||||
@Column(name = "color")
|
||||
public Integer color;
|
||||
@@ -69,10 +66,13 @@ public class Habit extends Model
|
||||
public Integer position;
|
||||
|
||||
@Column(name = "reminder_hour")
|
||||
public Integer reminder_hour;
|
||||
public Integer reminderHour;
|
||||
|
||||
@Column(name = "reminder_min")
|
||||
public Integer reminder_min;
|
||||
public Integer reminderMin;
|
||||
|
||||
@Column(name = "reminder_days")
|
||||
public Integer reminderDays;
|
||||
|
||||
@Column(name = "highlight")
|
||||
public Integer highlight;
|
||||
@@ -91,8 +91,9 @@ public class Habit extends Model
|
||||
this.position = Habit.getCount();
|
||||
this.highlight = 0;
|
||||
this.archived = 0;
|
||||
this.freq_den = 7;
|
||||
this.freq_num = 3;
|
||||
this.freqDen = 7;
|
||||
this.freqNum = 3;
|
||||
this.reminderDays = 127;
|
||||
}
|
||||
|
||||
public static Habit get(Long id)
|
||||
@@ -100,17 +101,10 @@ public class Habit extends Model
|
||||
return Habit.load(Habit.class, id);
|
||||
}
|
||||
|
||||
public static HashMap<Long, Habit> getAll()
|
||||
public static List<Habit> getAll(boolean includeArchive)
|
||||
{
|
||||
List<Habit> habits = select().execute();
|
||||
HashMap<Long, Habit> map = new HashMap<>();
|
||||
|
||||
for(Habit h : habits)
|
||||
{
|
||||
map.put(h.getId(), h);
|
||||
}
|
||||
|
||||
return map;
|
||||
if(includeArchive) return selectWithArchived().execute();
|
||||
else return select().execute();
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
@@ -121,21 +115,12 @@ public class Habit extends Model
|
||||
|
||||
protected static From select()
|
||||
{
|
||||
if(includeArchived)
|
||||
return new Select().from(Habit.class).orderBy("position");
|
||||
else
|
||||
return new Select().from(Habit.class).where("archived = 0").orderBy("position");
|
||||
return new Select().from(Habit.class).where("archived = 0").orderBy("position");
|
||||
}
|
||||
|
||||
public static void setIncludeArchived(boolean includeArchived)
|
||||
public static From selectWithArchived()
|
||||
{
|
||||
Habit.includeArchived = includeArchived;
|
||||
rebuildOrder();
|
||||
}
|
||||
|
||||
public static boolean isIncludeArchived()
|
||||
{
|
||||
return Habit.includeArchived;
|
||||
return new Select().from(Habit.class).orderBy("position");
|
||||
}
|
||||
|
||||
public static int getCount()
|
||||
@@ -143,19 +128,10 @@ public class Habit extends Model
|
||||
return select().count();
|
||||
}
|
||||
|
||||
public static Habit getByPosition(int position)
|
||||
{
|
||||
return select().offset(position).executeSingle();
|
||||
}
|
||||
|
||||
public static java.util.List<Habit> getHabits()
|
||||
{
|
||||
return select().execute();
|
||||
}
|
||||
|
||||
public static java.util.List<Habit> getHighlightedHabits()
|
||||
{
|
||||
return select().where("highlight = 1").orderBy("reminder_hour desc, reminder_min desc")
|
||||
return select().where("highlight = 1")
|
||||
.orderBy("reminder_hour desc, reminder_min desc")
|
||||
.execute();
|
||||
}
|
||||
|
||||
@@ -164,23 +140,30 @@ public class Habit extends Model
|
||||
return select().where("reminder_hour is not null").execute();
|
||||
}
|
||||
|
||||
public static void reorder(int from, int to)
|
||||
public static void reorder(Habit from, Habit to)
|
||||
{
|
||||
if (from == to) return;
|
||||
if(from == to) return;
|
||||
|
||||
Habit h = Habit.getByPosition(from);
|
||||
if (to < from) new Update(Habit.class).set("position = position + 1")
|
||||
.where("position >= ? and position < ?", to, from).execute();
|
||||
else new Update(Habit.class).set("position = position - 1")
|
||||
.where("position > ? and position <= ?", from, to).execute();
|
||||
if (to.position < from.position)
|
||||
{
|
||||
new Update(Habit.class).set("position = position + 1")
|
||||
.where("position >= ? and position < ?", to.position, from.position)
|
||||
.execute();
|
||||
}
|
||||
else
|
||||
{
|
||||
new Update(Habit.class).set("position = position - 1")
|
||||
.where("position > ? and position <= ?", from.position, to.position)
|
||||
.execute();
|
||||
}
|
||||
|
||||
h.position = to;
|
||||
h.save();
|
||||
from.position = to.position;
|
||||
from.save();
|
||||
}
|
||||
|
||||
public static void rebuildOrder()
|
||||
{
|
||||
List<Habit> habits = select().execute();
|
||||
List<Habit> habits = selectWithArchived().execute();
|
||||
|
||||
ActiveAndroid.beginTransaction();
|
||||
try
|
||||
@@ -193,51 +176,24 @@ public class Habit extends Model
|
||||
}
|
||||
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
} finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void roundTimestamps()
|
||||
{
|
||||
List<Repetition> reps = new Select().from(Repetition.class).execute();
|
||||
for (Repetition r : reps)
|
||||
{
|
||||
r.timestamp = DateHelper.getStartOfDay(r.timestamp);
|
||||
r.save();
|
||||
}
|
||||
}
|
||||
|
||||
public static void recomputeAllScores()
|
||||
{
|
||||
for (Habit habit : getHabits())
|
||||
{
|
||||
habit.deleteScoresNewerThan(0);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getStarCount()
|
||||
{
|
||||
String args[] = {};
|
||||
return SQLiteUtils.intQuery("select count(*) from (select score, max(timestamp) from " +
|
||||
"score group by habit) as scores where scores.score >= " +
|
||||
Integer.toString(12973000), args);
|
||||
|
||||
}
|
||||
|
||||
public void copyAttributes(Habit model)
|
||||
{
|
||||
this.name = model.name;
|
||||
this.description = model.description;
|
||||
this.freq_num = model.freq_num;
|
||||
this.freq_den = model.freq_den;
|
||||
this.freqNum = model.freqNum;
|
||||
this.freqDen = model.freqDen;
|
||||
this.color = model.color;
|
||||
this.position = model.position;
|
||||
this.reminder_hour = model.reminder_hour;
|
||||
this.reminder_min = model.reminder_min;
|
||||
this.reminderHour = model.reminderHour;
|
||||
this.reminderMin = model.reminderMin;
|
||||
this.reminderDays = model.reminderDays;
|
||||
this.highlight = model.highlight;
|
||||
this.archived = model.archived;
|
||||
}
|
||||
@@ -264,15 +220,33 @@ public class Habit extends Model
|
||||
return (count > 0);
|
||||
}
|
||||
|
||||
public boolean hasRepToday()
|
||||
public void cascadeDelete()
|
||||
{
|
||||
return hasRep(DateHelper.getStartOfToday());
|
||||
Long id = getId();
|
||||
|
||||
ActiveAndroid.beginTransaction();
|
||||
try
|
||||
{
|
||||
new Delete().from(Checkmark.class).where("habit = ?", id).execute();
|
||||
new Delete().from(Repetition.class).where("habit = ?", id).execute();
|
||||
new Delete().from(Score.class).where("habit = ?", id).execute();
|
||||
new Delete().from(Streak.class).where("habit = ?", id).execute();
|
||||
delete();
|
||||
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteReps(long timestamp)
|
||||
{
|
||||
new Delete().from(Repetition.class).where("habit = ?", getId())
|
||||
.and("timestamp = ?", timestamp).execute();
|
||||
new Delete().from(Repetition.class)
|
||||
.where("habit = ?", getId())
|
||||
.and("timestamp = ?", timestamp)
|
||||
.execute();
|
||||
}
|
||||
|
||||
public void deleteCheckmarksNewerThan(long timestamp)
|
||||
@@ -306,7 +280,7 @@ public class Habit extends Model
|
||||
int nDays = (int) ((toTimestamp - fromTimestamp) / day) + 1;
|
||||
int[] checks = new int[nDays];
|
||||
|
||||
if(cursor.moveToFirst())
|
||||
if (cursor.moveToFirst())
|
||||
{
|
||||
do
|
||||
{
|
||||
@@ -327,7 +301,7 @@ public class Habit extends Model
|
||||
long day = DateHelper.millisecondsInOneDay;
|
||||
|
||||
Checkmark newestCheckmark = getNewestCheckmark();
|
||||
if(newestCheckmark == null)
|
||||
if (newestCheckmark == null)
|
||||
{
|
||||
Repetition oldestRep = getOldestRep();
|
||||
if (oldestRep == null) return;
|
||||
@@ -339,10 +313,9 @@ public class Habit extends Model
|
||||
beginning = newestCheckmark.timestamp + day;
|
||||
}
|
||||
|
||||
if(beginning > today)
|
||||
return;
|
||||
if (beginning > today) return;
|
||||
|
||||
long beginningExtended = beginning - (long) (freq_den) * day;
|
||||
long beginningExtended = beginning - (long) (freqDen) * day;
|
||||
List<Repetition> reps = selectRepsFromTo(beginningExtended, today).execute();
|
||||
|
||||
int nDays = (int) ((today - beginning) / day) + 1;
|
||||
@@ -362,10 +335,10 @@ public class Habit extends Model
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
for (int j = 0; j < freq_den; j++)
|
||||
for (int j = 0; j < freqDen; j++)
|
||||
if (checks[i + j] == 2) counter++;
|
||||
|
||||
if (counter >= freq_num) checks[i] = Math.max(checks[i], 1);
|
||||
if (counter >= freqNum) checks[i] = Math.max(checks[i], 1);
|
||||
}
|
||||
|
||||
ActiveAndroid.beginTransaction();
|
||||
@@ -382,8 +355,7 @@ public class Habit extends Model
|
||||
}
|
||||
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
} finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
}
|
||||
@@ -419,10 +391,7 @@ public class Habit extends Model
|
||||
|
||||
public Repetition getOldestRepNewerThan(long timestamp)
|
||||
{
|
||||
return selectReps()
|
||||
.where("timestamp > ?", timestamp)
|
||||
.limit(1)
|
||||
.executeSingle();
|
||||
return selectReps().where("timestamp > ?", timestamp).limit(1).executeSingle();
|
||||
}
|
||||
|
||||
public void toggleRepetition(long timestamp)
|
||||
@@ -447,10 +416,7 @@ public class Habit extends Model
|
||||
public void archive()
|
||||
{
|
||||
archived = 1;
|
||||
position = 9999;
|
||||
save();
|
||||
|
||||
Habit.rebuildOrder();
|
||||
}
|
||||
|
||||
public void unarchive()
|
||||
@@ -471,13 +437,18 @@ public class Habit extends Model
|
||||
|
||||
public Score getNewestScore()
|
||||
{
|
||||
return new Select().from(Score.class).where("habit = ?", getId()).orderBy("timestamp desc")
|
||||
.limit(1).executeSingle();
|
||||
return new Select().from(Score.class)
|
||||
.where("habit = ?", getId())
|
||||
.orderBy("timestamp desc")
|
||||
.limit(1)
|
||||
.executeSingle();
|
||||
}
|
||||
|
||||
public void deleteScoresNewerThan(long timestamp)
|
||||
{
|
||||
new Delete().from(Score.class).where("habit = ?", getId()).and("timestamp >= ?", timestamp)
|
||||
new Delete().from(Score.class)
|
||||
.where("habit = ?", getId())
|
||||
.and("timestamp >= ?", timestamp)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@@ -489,7 +460,7 @@ public class Habit extends Model
|
||||
long today = DateHelper.getStartOfDay(DateHelper.getLocalTime());
|
||||
long day = DateHelper.millisecondsInOneDay;
|
||||
|
||||
double freq = ((double) freq_num) / freq_den;
|
||||
double freq = ((double) freqNum) / freqDen;
|
||||
double multiplier = Math.pow(0.5, 1.0 / (14.0 / freq - 1));
|
||||
|
||||
Score newestScore = getNewestScore();
|
||||
@@ -525,7 +496,7 @@ public class Habit extends Model
|
||||
if (reps[reps.length - i - 1] == 2)
|
||||
{
|
||||
s.score += 1000000;
|
||||
s.score = Math.min(s.score, 19259500);
|
||||
s.score = Math.min(s.score, MAX_SCORE);
|
||||
}
|
||||
s.save();
|
||||
|
||||
@@ -533,8 +504,7 @@ public class Habit extends Model
|
||||
}
|
||||
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
} finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
}
|
||||
@@ -542,24 +512,20 @@ public class Habit extends Model
|
||||
return lastScore;
|
||||
}
|
||||
|
||||
public List<Score> getScores(long fromTimestamp, long toTimestamp)
|
||||
{
|
||||
return getScores(fromTimestamp, toTimestamp, 1, 0);
|
||||
}
|
||||
|
||||
public List<Score> getScores(long fromTimestamp, long toTimestamp, int divisor, long offset)
|
||||
{
|
||||
return new Select().from(Score.class).where("habit = ? and timestamp > ? and " +
|
||||
"timestamp <= ? and (timestamp - ?) % ? = 0", getId(), fromTimestamp, toTimestamp,
|
||||
offset, divisor).execute();
|
||||
return new Select().from(Score.class)
|
||||
.where("habit = ? and timestamp > ? and " +
|
||||
"timestamp <= ? and (timestamp - ?) % ? = 0", getId(), fromTimestamp,
|
||||
toTimestamp, offset, divisor)
|
||||
.execute();
|
||||
}
|
||||
|
||||
public List<Streak> getStreaks()
|
||||
{
|
||||
updateStreaks();
|
||||
|
||||
return new Select()
|
||||
.from(Streak.class)
|
||||
return new Select().from(Streak.class)
|
||||
.where("habit = ?", getId())
|
||||
.orderBy("end asc")
|
||||
.execute();
|
||||
@@ -567,8 +533,7 @@ public class Habit extends Model
|
||||
|
||||
public Streak getNewestStreak()
|
||||
{
|
||||
return new Select()
|
||||
.from(Streak.class)
|
||||
return new Select().from(Streak.class)
|
||||
.where("habit = ?", getId())
|
||||
.orderBy("end desc")
|
||||
.limit(1)
|
||||
@@ -582,7 +547,7 @@ public class Habit extends Model
|
||||
long day = DateHelper.millisecondsInOneDay;
|
||||
|
||||
Streak newestStreak = getNewestStreak();
|
||||
if(newestStreak == null)
|
||||
if (newestStreak == null)
|
||||
{
|
||||
Repetition oldestRep = getOldestRep();
|
||||
if (oldestRep == null) return;
|
||||
@@ -597,7 +562,7 @@ public class Habit extends Model
|
||||
beginning = oldestRep.timestamp;
|
||||
}
|
||||
|
||||
if(beginning > today) return;
|
||||
if (beginning > today) return;
|
||||
|
||||
int checks[] = getCheckmarks(beginning, today);
|
||||
ArrayList<Long> list = new ArrayList<>();
|
||||
@@ -605,17 +570,16 @@ public class Habit extends Model
|
||||
long current = beginning;
|
||||
list.add(current);
|
||||
|
||||
for(int i = 1; i < checks.length; i++)
|
||||
for (int i = 1; i < checks.length; i++)
|
||||
{
|
||||
current += day;
|
||||
int j = checks.length - i - 1;
|
||||
|
||||
if((checks[j + 1] == 0 && checks[j] > 0)) list.add(current);
|
||||
if((checks[j + 1] > 0 && checks[j] == 0)) list.add(current - day);
|
||||
if ((checks[j + 1] == 0 && checks[j] > 0)) list.add(current);
|
||||
if ((checks[j + 1] > 0 && checks[j] == 0)) list.add(current - day);
|
||||
}
|
||||
|
||||
if(list.size() % 2 == 1)
|
||||
list.add(current);
|
||||
if (list.size() % 2 == 1) list.add(current);
|
||||
|
||||
ActiveAndroid.beginTransaction();
|
||||
|
||||
@@ -632,8 +596,7 @@ public class Habit extends Model
|
||||
}
|
||||
|
||||
ActiveAndroid.setTransactionSuccessful();
|
||||
}
|
||||
finally
|
||||
} finally
|
||||
{
|
||||
ActiveAndroid.endTransaction();
|
||||
}
|
||||
@@ -657,7 +620,8 @@ public class Habit extends Model
|
||||
{
|
||||
savedHabit.save();
|
||||
savedId = savedHabit.getId();
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
savedHabit.save(savedId);
|
||||
}
|
||||
@@ -696,8 +660,8 @@ public class Habit extends Model
|
||||
this.modified = new Habit(modified);
|
||||
this.original = new Habit(Habit.this);
|
||||
|
||||
hasIntervalChanged = (this.original.freq_den != this.modified.freq_den ||
|
||||
this.original.freq_num != this.modified.freq_num);
|
||||
hasIntervalChanged = (this.original.freqDen != this.modified.freqDen ||
|
||||
this.original.freqNum != this.modified.freqNum);
|
||||
}
|
||||
|
||||
public void execute()
|
||||
@@ -758,54 +722,4 @@ public class Habit extends Model
|
||||
execute();
|
||||
}
|
||||
}
|
||||
|
||||
public class ArchiveCommand extends Command
|
||||
{
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
archive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
unarchive();
|
||||
}
|
||||
|
||||
public Integer getExecuteStringId()
|
||||
{
|
||||
return R.string.toast_habit_archived;
|
||||
}
|
||||
|
||||
public Integer getUndoStringId()
|
||||
{
|
||||
return R.string.toast_habit_unarchived;
|
||||
}
|
||||
}
|
||||
|
||||
public class UnarchiveCommand extends Command
|
||||
{
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
unarchive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undo()
|
||||
{
|
||||
archive();
|
||||
}
|
||||
|
||||
public Integer getExecuteStringId()
|
||||
{
|
||||
return R.string.toast_habit_unarchived;
|
||||
}
|
||||
|
||||
public Integer getUndoStringId()
|
||||
{
|
||||
return R.string.toast_habit_archived;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,12 @@ import com.activeandroid.annotation.Column;
|
||||
import com.activeandroid.annotation.Table;
|
||||
|
||||
@Table(name = "Repetitions")
|
||||
public class Repetition extends Model {
|
||||
public class Repetition extends Model
|
||||
{
|
||||
|
||||
@Column(name = "habit")
|
||||
public Habit habit;
|
||||
|
||||
@Column(name = "timestamp")
|
||||
public Long timestamp;
|
||||
@Column(name = "habit")
|
||||
public Habit habit;
|
||||
|
||||
@Column(name = "timestamp")
|
||||
public Long timestamp;
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ import com.activeandroid.annotation.Table;
|
||||
@Table(name = "Score")
|
||||
public class Score extends Model
|
||||
{
|
||||
@Column(name = "habit")
|
||||
public Habit habit;
|
||||
|
||||
@Column(name = "timestamp")
|
||||
public Long timestamp;
|
||||
|
||||
@Column(name = "score")
|
||||
public Integer score;
|
||||
@Column(name = "habit")
|
||||
public Habit habit;
|
||||
|
||||
@Column(name = "timestamp")
|
||||
public Long timestamp;
|
||||
|
||||
@Column(name = "score")
|
||||
public Integer score;
|
||||
}
|
||||
|
||||
@@ -22,52 +22,93 @@ import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.Align;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v4.view.MotionEventCompat;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.models.Checkmark;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
|
||||
public class HabitHistoryView extends View
|
||||
public class HabitHistoryView extends ScrollableDataView
|
||||
{
|
||||
|
||||
private Habit habit;
|
||||
private int[] checks;
|
||||
|
||||
private Context context;
|
||||
private int[] checkmarks;
|
||||
private Paint pSquareBg, pSquareFg, pTextHeader;
|
||||
private int squareSpacing;
|
||||
|
||||
private int squareSize, squareSpacing;
|
||||
private int nColumns, offsetWeeks;
|
||||
private float squareTextOffset;
|
||||
private float headerTextOffset;
|
||||
|
||||
private int colorPrimary, colorPrimaryBrighter, grey;
|
||||
private Float prevX, prevY;
|
||||
private String wdays[];
|
||||
private SimpleDateFormat dfMonth;
|
||||
private SimpleDateFormat dfYear;
|
||||
|
||||
public HabitHistoryView(Context context, Habit habit, int squareSize)
|
||||
private Calendar baseDate;
|
||||
private int nDays;
|
||||
private int todayWeekday;
|
||||
private int colors[];
|
||||
|
||||
public HabitHistoryView(Context context, Habit habit, int baseSize)
|
||||
{
|
||||
super(context);
|
||||
this.habit = habit;
|
||||
this.context = context;
|
||||
this.squareSize = squareSize;
|
||||
|
||||
colorPrimary = habit.color;
|
||||
colorPrimaryBrighter = ColorHelper.mixColors(colorPrimary, Color.WHITE, 0.5f);
|
||||
grey = Color.rgb(230, 230, 230);
|
||||
setDimensions(baseSize);
|
||||
createPaints();
|
||||
createColors();
|
||||
|
||||
wdays = DateHelper.getShortDayNames();
|
||||
dfMonth = new SimpleDateFormat("MMM");
|
||||
dfYear = new SimpleDateFormat("yyyy");
|
||||
}
|
||||
|
||||
private void updateDate()
|
||||
{
|
||||
baseDate = new GregorianCalendar();
|
||||
baseDate.add(Calendar.DAY_OF_YEAR, -(dataOffset - 1) * 7);
|
||||
|
||||
nDays = (nColumns - 1) * 7;
|
||||
todayWeekday = new GregorianCalendar().get(Calendar.DAY_OF_WEEK) % 7;
|
||||
|
||||
baseDate.add(Calendar.DAY_OF_YEAR, -nDays);
|
||||
baseDate.add(Calendar.DAY_OF_YEAR, -todayWeekday);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||
{
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
updateDate();
|
||||
}
|
||||
|
||||
private void createColors()
|
||||
{
|
||||
int primaryColor = habit.color;
|
||||
int primaryColorBright = ColorHelper.mixColors(primaryColor, Color.WHITE, 0.5f);
|
||||
int grey = Color.rgb(230, 230, 230);
|
||||
|
||||
colors = new int[3];
|
||||
colors[0] = grey;
|
||||
colors[1] = primaryColorBright;
|
||||
colors[2] = primaryColor;
|
||||
}
|
||||
|
||||
private void setDimensions(int baseSize)
|
||||
{
|
||||
columnWidth = baseSize;
|
||||
columnHeight = 8 * baseSize;
|
||||
squareSpacing = 2;
|
||||
}
|
||||
|
||||
private void createPaints()
|
||||
{
|
||||
pTextHeader = new Paint();
|
||||
pTextHeader.setColor(Color.LTGRAY);
|
||||
pTextHeader.setTextAlign(Align.LEFT);
|
||||
pTextHeader.setTextSize(squareSize * 0.5f);
|
||||
pTextHeader.setTextSize(columnWidth * 0.5f);
|
||||
pTextHeader.setAntiAlias(true);
|
||||
|
||||
pSquareBg = new Paint();
|
||||
@@ -76,178 +117,123 @@ public class HabitHistoryView extends View
|
||||
pSquareFg = new Paint();
|
||||
pSquareFg.setColor(Color.WHITE);
|
||||
pSquareFg.setAntiAlias(true);
|
||||
pSquareFg.setTextSize(squareSize * 0.5f);
|
||||
pSquareFg.setTextSize(columnWidth * 0.5f);
|
||||
pSquareFg.setTextAlign(Align.CENTER);
|
||||
|
||||
squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
|
||||
headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredWidth(), 8 * squareSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||
{
|
||||
nColumns = (w / squareSize) - 1;
|
||||
fetchReps();
|
||||
}
|
||||
|
||||
private void fetchReps()
|
||||
protected void fetchData()
|
||||
{
|
||||
Calendar currentDate = new GregorianCalendar();
|
||||
currentDate.add(Calendar.DAY_OF_YEAR, -offsetWeeks * 7);
|
||||
currentDate.add(Calendar.DAY_OF_YEAR, -dataOffset * 7);
|
||||
int dayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK) % 7;
|
||||
|
||||
long dateTo = DateHelper.getStartOfToday();
|
||||
for (int i = 0; i < 7 - dayOfWeek; i++)
|
||||
dateTo += DateHelper.millisecondsInOneDay;
|
||||
|
||||
for (int i = 0; i < offsetWeeks * 7; i++)
|
||||
for (int i = 0; i < dataOffset * 7; i++)
|
||||
dateTo -= DateHelper.millisecondsInOneDay;
|
||||
|
||||
long dateFrom = dateTo;
|
||||
for (int i = 0; i < nColumns * 7; i++)
|
||||
for (int i = 0; i < (nColumns - 1) * 7; i++)
|
||||
dateFrom -= DateHelper.millisecondsInOneDay;
|
||||
|
||||
checks = habit.getCheckmarks(dateFrom, dateTo);
|
||||
checkmarks = habit.getCheckmarks(dateFrom, dateTo);
|
||||
updateDate();
|
||||
}
|
||||
|
||||
private String previousMonth;
|
||||
private String previousYear;
|
||||
private boolean justPrintedYear;
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas)
|
||||
{
|
||||
super.onDraw(canvas);
|
||||
|
||||
Rect square = new Rect(0, 0, squareSize - squareSpacing, squareSize - squareSpacing);
|
||||
Rect location = new Rect(0, 0, columnWidth - squareSpacing, columnWidth - squareSpacing);
|
||||
|
||||
Calendar currentDate = new GregorianCalendar();
|
||||
currentDate.add(Calendar.DAY_OF_YEAR, - (offsetWeeks - 1) * 7);
|
||||
previousMonth = "";
|
||||
previousYear = "";
|
||||
justPrintedYear = false;
|
||||
|
||||
int nDays = nColumns * 7;
|
||||
int todayWeekday = new GregorianCalendar().get(Calendar.DAY_OF_WEEK) % 7;
|
||||
GregorianCalendar currentDate = (GregorianCalendar) baseDate.clone();
|
||||
|
||||
currentDate.add(Calendar.DAY_OF_YEAR, -nDays);
|
||||
currentDate.add(Calendar.DAY_OF_YEAR, -todayWeekday);
|
||||
|
||||
SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
|
||||
SimpleDateFormat dfYear = new SimpleDateFormat("yyyy");
|
||||
|
||||
String previousMonth = "";
|
||||
String previousYear = "";
|
||||
|
||||
int colors[] = {grey, colorPrimaryBrighter, colorPrimary};
|
||||
String markers[] =
|
||||
{context.getString(R.string.fa_times), context.getString(R.string.fa_check),
|
||||
context.getString(R.string.fa_check)};
|
||||
|
||||
float squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
|
||||
float headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
|
||||
boolean justPrintedYear = false;
|
||||
|
||||
int k = nDays;
|
||||
for (int i = 0; i < nColumns; i++)
|
||||
for (int column = 0; column < nColumns - 1; column++)
|
||||
{
|
||||
String month = dfMonth.format(currentDate.getTime());
|
||||
String year = dfYear.format(currentDate.getTime());
|
||||
|
||||
if (!month.equals(previousMonth))
|
||||
{
|
||||
int offset = 0;
|
||||
if (justPrintedYear) offset += squareSize;
|
||||
|
||||
canvas.drawText(month, square.left + offset, square.bottom - headerTextOffset,
|
||||
pTextHeader);
|
||||
previousMonth = month;
|
||||
justPrintedYear = false;
|
||||
} else if (!year.equals(previousYear))
|
||||
{
|
||||
canvas.drawText(year, square.left, square.bottom - headerTextOffset, pTextHeader);
|
||||
previousYear = year;
|
||||
justPrintedYear = true;
|
||||
} else
|
||||
{
|
||||
justPrintedYear = false;
|
||||
}
|
||||
|
||||
|
||||
square.offset(0, squareSize);
|
||||
|
||||
for (int j = 0; j < 7; j++)
|
||||
{
|
||||
if (!(i == nColumns - 1 && offsetWeeks == 0 && j > todayWeekday))
|
||||
{
|
||||
if(k >= checks.length) pSquareBg.setColor(colors[0]);
|
||||
else pSquareBg.setColor(colors[checks[k]]);
|
||||
|
||||
canvas.drawRect(square, pSquareBg);
|
||||
canvas.drawText(Integer.toString(currentDate.get(Calendar.DAY_OF_MONTH)),
|
||||
square.centerX(), square.centerY() + squareTextOffset, pSquareFg);
|
||||
}
|
||||
|
||||
currentDate.add(Calendar.DAY_OF_MONTH, 1);
|
||||
square.offset(0, squareSize);
|
||||
k--;
|
||||
}
|
||||
|
||||
square.offset(squareSize, -8 * squareSize);
|
||||
drawColumn(canvas, location, currentDate, column);
|
||||
location.offset(columnWidth, -columnHeight);
|
||||
}
|
||||
|
||||
String wdays[] = {"Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"};
|
||||
drawAxis(canvas, location);
|
||||
}
|
||||
|
||||
private void drawColumn(Canvas canvas, Rect location, GregorianCalendar date, int column)
|
||||
{
|
||||
drawColumnHeader(canvas, location, date);
|
||||
location.offset(0, columnWidth);
|
||||
|
||||
for (int j = 0; j < 7; j++)
|
||||
{
|
||||
if (!(column == nColumns - 2 && dataOffset == 0 && j > todayWeekday))
|
||||
{
|
||||
int checkmarkOffset = nDays - 7 * column - j;
|
||||
drawSquare(canvas, location, date, checkmarkOffset);
|
||||
}
|
||||
|
||||
date.add(Calendar.DAY_OF_MONTH, 1);
|
||||
location.offset(0, columnWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawSquare(Canvas canvas, Rect location, GregorianCalendar date,
|
||||
int checkmarkOffset)
|
||||
{
|
||||
if (checkmarkOffset >= checkmarks.length) pSquareBg.setColor(colors[0]);
|
||||
else pSquareBg.setColor(colors[checkmarks[checkmarkOffset]]);
|
||||
|
||||
canvas.drawRect(location, pSquareBg);
|
||||
String text = Integer.toString(date.get(Calendar.DAY_OF_MONTH));
|
||||
canvas.drawText(text, location.centerX(), location.centerY() + squareTextOffset, pSquareFg);
|
||||
}
|
||||
|
||||
private void drawAxis(Canvas canvas, Rect location)
|
||||
{
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
square.offset(0, squareSize);
|
||||
canvas.drawText(wdays[i], square.left + headerTextOffset,
|
||||
square.bottom - headerTextOffset, pTextHeader);
|
||||
location.offset(0, columnWidth);
|
||||
canvas.drawText(wdays[i], location.left + headerTextOffset,
|
||||
location.bottom - headerTextOffset, pTextHeader);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event)
|
||||
private void drawColumnHeader(Canvas canvas, Rect location, GregorianCalendar date)
|
||||
{
|
||||
int action = event.getAction();
|
||||
String month = dfMonth.format(date.getTime());
|
||||
String year = dfYear.format(date.getTime());
|
||||
|
||||
int pointerIndex = MotionEventCompat.getActionIndex(event);
|
||||
final float x = MotionEventCompat.getX(event, pointerIndex);
|
||||
final float y = MotionEventCompat.getY(event, pointerIndex);
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN)
|
||||
if (!month.equals(previousMonth))
|
||||
{
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
int offset = 0;
|
||||
if (justPrintedYear) offset += columnWidth;
|
||||
|
||||
canvas.drawText(month, location.left + offset, location.bottom - headerTextOffset,
|
||||
pTextHeader);
|
||||
previousMonth = month;
|
||||
justPrintedYear = false;
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_MOVE)
|
||||
else if (!year.equals(previousYear))
|
||||
{
|
||||
float dx = x - prevX;
|
||||
float dy = y - prevY;
|
||||
|
||||
if (Math.abs(dy) > Math.abs(dx)) return false;
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
if(move(dx))
|
||||
{
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean move(float dx)
|
||||
{
|
||||
int newOffsetWeeks = offsetWeeks + (int) (dx / squareSize);
|
||||
newOffsetWeeks = Math.max(0, newOffsetWeeks);
|
||||
|
||||
if (newOffsetWeeks != offsetWeeks)
|
||||
{
|
||||
offsetWeeks = newOffsetWeeks;
|
||||
fetchReps();
|
||||
invalidate();
|
||||
return true;
|
||||
canvas.drawText(year, location.left, location.bottom - headerTextOffset, pTextHeader);
|
||||
previousYear = year;
|
||||
justPrintedYear = true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
{
|
||||
justPrintedYear = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.support.v4.view.MotionEventCompat;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
@@ -33,30 +30,23 @@ import org.isoron.uhabits.models.Score;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
|
||||
public class HabitScoreView extends View
|
||||
public class HabitScoreView extends ScrollableDataView
|
||||
{
|
||||
public static final int BUCKET_SIZE = 7;
|
||||
|
||||
private final Paint pGrid;
|
||||
private final float em;
|
||||
private Habit habit;
|
||||
private int columnWidth, columnHeight, nColumns;
|
||||
|
||||
private Paint pText, pGraph;
|
||||
private int dataOffset;
|
||||
|
||||
private int barHeaderHeight;
|
||||
|
||||
private int[] colors;
|
||||
private float prevX;
|
||||
private float prevY;
|
||||
private List<Score> scores;
|
||||
|
||||
public HabitScoreView(Context context, Habit habit, int columnWidth)
|
||||
{
|
||||
super(context);
|
||||
this.habit = habit;
|
||||
this.columnWidth = columnWidth;
|
||||
|
||||
pText = new Paint();
|
||||
pText.setColor(Color.LTGRAY);
|
||||
@@ -75,8 +65,11 @@ public class HabitScoreView extends View
|
||||
pGrid.setAntiAlias(true);
|
||||
pGrid.setStrokeWidth(columnWidth * 0.05f);
|
||||
|
||||
this.columnWidth = columnWidth;
|
||||
columnHeight = 8 * columnWidth;
|
||||
barHeaderHeight = columnWidth;
|
||||
headerHeight = columnWidth;
|
||||
footerHeight = columnWidth;
|
||||
|
||||
em = pText.getFontSpacing();
|
||||
|
||||
colors = new int[4];
|
||||
@@ -87,7 +80,7 @@ public class HabitScoreView extends View
|
||||
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
|
||||
}
|
||||
|
||||
private void fetchScores()
|
||||
protected void fetchData()
|
||||
{
|
||||
|
||||
long toTimestamp = DateHelper.getStartOfToday();
|
||||
@@ -98,23 +91,8 @@ public class HabitScoreView extends View
|
||||
for (int i = 0; i < nColumns * BUCKET_SIZE; i++)
|
||||
fromTimestamp -= DateHelper.millisecondsInOneDay;
|
||||
|
||||
scores = habit.getScores(fromTimestamp, toTimestamp, BUCKET_SIZE * DateHelper.millisecondsInOneDay,
|
||||
toTimestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredWidth(), columnHeight + 2 * barHeaderHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||
{
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
nColumns = w / columnWidth;
|
||||
fetchScores();
|
||||
scores = habit.getScores(fromTimestamp, toTimestamp,
|
||||
BUCKET_SIZE * DateHelper.millisecondsInOneDay, toTimestamp);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,10 +101,9 @@ public class HabitScoreView extends View
|
||||
super.onDraw(canvas);
|
||||
|
||||
float lineHeight = pText.getFontSpacing();
|
||||
float barHeaderOffset = lineHeight * 0.4f;
|
||||
|
||||
RectF rGrid = new RectF(0, 0, nColumns * columnWidth, columnHeight);
|
||||
rGrid.offset(0, barHeaderHeight);
|
||||
rGrid.offset(0, headerHeight);
|
||||
drawGrid(canvas, rGrid);
|
||||
|
||||
SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
|
||||
@@ -150,7 +127,7 @@ public class HabitScoreView extends View
|
||||
|
||||
RectF r = new RectF(0, 0, columnWidth, columnWidth);
|
||||
r.offset(offset * columnWidth,
|
||||
barHeaderHeight + columnHeight - height - columnWidth / 2);
|
||||
headerHeight + columnHeight - height - columnWidth / 2);
|
||||
|
||||
if (prevR != null)
|
||||
{
|
||||
@@ -163,14 +140,11 @@ public class HabitScoreView extends View
|
||||
prevR = r;
|
||||
|
||||
r = new RectF(0, 0, columnWidth, columnHeight);
|
||||
r.offset(offset * columnWidth, barHeaderHeight);
|
||||
r.offset(offset * columnWidth, headerHeight);
|
||||
if (!month.equals(previousMonth))
|
||||
{
|
||||
canvas.drawText(month, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
|
||||
} else
|
||||
{
|
||||
else
|
||||
canvas.drawText(day, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
|
||||
}
|
||||
|
||||
previousMonth = month;
|
||||
|
||||
@@ -179,10 +153,6 @@ public class HabitScoreView extends View
|
||||
|
||||
private void drawGrid(Canvas canvas, RectF rGrid)
|
||||
{
|
||||
// pGrid.setColor(Color.rgb(230, 230, 230));
|
||||
// pGrid.setStyle(Paint.Style.STROKE);
|
||||
// canvas.drawRect(rGrid, pGrid);
|
||||
|
||||
int nRows = 5;
|
||||
float rowHeight = rGrid.height() / nRows;
|
||||
|
||||
@@ -219,50 +189,4 @@ public class HabitScoreView extends View
|
||||
pGraph.setColor(Color.WHITE);
|
||||
canvas.drawOval(rect, pGraph);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event)
|
||||
{
|
||||
int action = event.getAction();
|
||||
|
||||
int pointerIndex = MotionEventCompat.getActionIndex(event);
|
||||
final float x = MotionEventCompat.getX(event, pointerIndex);
|
||||
final float y = MotionEventCompat.getY(event, pointerIndex);
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN)
|
||||
{
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_MOVE)
|
||||
{
|
||||
float dx = x - prevX;
|
||||
float dy = y - prevY;
|
||||
|
||||
if (Math.abs(dy) > Math.abs(dx)) return false;
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
if (move(dx))
|
||||
{
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean move(float dx)
|
||||
{
|
||||
int newDataOffset = dataOffset + (int) (dx / columnWidth);
|
||||
newDataOffset = Math.max(0, newDataOffset);
|
||||
|
||||
if (newDataOffset != dataOffset)
|
||||
{
|
||||
dataOffset = newDataOffset;
|
||||
fetchScores();
|
||||
invalidate();
|
||||
return true;
|
||||
} else return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,41 +21,51 @@ import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.support.v4.view.MotionEventCompat;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.uhabits.models.Habit;
|
||||
import org.isoron.uhabits.models.Streak;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.List;
|
||||
|
||||
public class HabitStreakView extends View
|
||||
public class HabitStreakView extends ScrollableDataView
|
||||
{
|
||||
private Habit habit;
|
||||
private int columnWidth, columnHeight, nColumns;
|
||||
|
||||
private Paint pText, pBar;
|
||||
private List<Streak> streaks;
|
||||
private int dataOffset;
|
||||
|
||||
private long maxStreakLength;
|
||||
|
||||
private int barHeaderHeight;
|
||||
|
||||
private int[] colors;
|
||||
private float prevX;
|
||||
private float prevY;
|
||||
|
||||
public HabitStreakView(Context context, Habit habit, int columnWidth)
|
||||
{
|
||||
super(context);
|
||||
this.habit = habit;
|
||||
this.columnWidth = columnWidth;
|
||||
|
||||
setDimensions(columnWidth);
|
||||
createPaints();
|
||||
createColors();
|
||||
}
|
||||
|
||||
private void setDimensions(int baseSize)
|
||||
{
|
||||
this.columnWidth = baseSize;
|
||||
columnHeight = 8 * baseSize;
|
||||
headerHeight = baseSize;
|
||||
footerHeight = baseSize;
|
||||
}
|
||||
|
||||
private void createColors()
|
||||
{
|
||||
colors = new int[4];
|
||||
colors[0] = Color.rgb(230, 230, 230);
|
||||
colors[3] = habit.color;
|
||||
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);
|
||||
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
|
||||
}
|
||||
|
||||
private void createPaints()
|
||||
{
|
||||
pText = new Paint();
|
||||
pText.setColor(Color.LTGRAY);
|
||||
pText.setTextAlign(Paint.Align.CENTER);
|
||||
@@ -66,41 +76,16 @@ public class HabitStreakView extends View
|
||||
pBar.setTextAlign(Paint.Align.CENTER);
|
||||
pBar.setTextSize(columnWidth * 0.5f);
|
||||
pBar.setAntiAlias(true);
|
||||
|
||||
columnHeight = 8 * columnWidth;
|
||||
barHeaderHeight = columnWidth;
|
||||
|
||||
colors = new int[4];
|
||||
|
||||
colors[0] = Color.rgb(230, 230, 230);
|
||||
colors[3] = habit.color;
|
||||
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);
|
||||
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
|
||||
|
||||
fetchStreaks();
|
||||
}
|
||||
|
||||
private void fetchStreaks()
|
||||
protected void fetchData()
|
||||
{
|
||||
streaks = habit.getStreaks();
|
||||
|
||||
for(Streak s : streaks)
|
||||
for (Streak s : streaks)
|
||||
maxStreakLength = Math.max(maxStreakLength, s.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredWidth(), columnHeight + 2*barHeaderHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||
{
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
nColumns = w / columnWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas)
|
||||
@@ -116,73 +101,26 @@ public class HabitStreakView extends View
|
||||
|
||||
String previousMonth = "";
|
||||
|
||||
for (int offset = 0; offset < nColumns && start+offset < nStreaks; offset++)
|
||||
for (int offset = 0; offset < nColumns && start + offset < nStreaks; offset++)
|
||||
{
|
||||
String month = dfMonth.format(streaks.get(start+offset).start);
|
||||
String month = dfMonth.format(streaks.get(start + offset).start);
|
||||
|
||||
long l = streaks.get(offset+start).length;
|
||||
long l = streaks.get(offset + start).length;
|
||||
double lRelative = ((double) l) / maxStreakLength;
|
||||
|
||||
pBar.setColor(colors[(int) Math.floor(lRelative*3)]);
|
||||
pBar.setColor(colors[(int) Math.floor(lRelative * 3)]);
|
||||
|
||||
int height = (int) (columnHeight * lRelative);
|
||||
Rect r = new Rect(0,0,columnWidth-2, height);
|
||||
r.offset(offset * columnWidth, barHeaderHeight + columnHeight - height);
|
||||
Rect r = new Rect(0, 0, columnWidth - 2, height);
|
||||
r.offset(offset * columnWidth, headerHeight + columnHeight - height);
|
||||
|
||||
canvas.drawRect(r, pBar);
|
||||
canvas.drawText(Long.toString(l), r.centerX(), r.top - barHeaderOffset, pBar);
|
||||
|
||||
if(!month.equals(previousMonth))
|
||||
if (!month.equals(previousMonth))
|
||||
canvas.drawText(month, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
|
||||
|
||||
previousMonth = month;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event)
|
||||
{
|
||||
int action = event.getAction();
|
||||
|
||||
int pointerIndex = MotionEventCompat.getActionIndex(event);
|
||||
final float x = MotionEventCompat.getX(event, pointerIndex);
|
||||
final float y = MotionEventCompat.getY(event, pointerIndex);
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN)
|
||||
{
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_MOVE)
|
||||
{
|
||||
float dx = x - prevX;
|
||||
float dy = y - prevY;
|
||||
|
||||
if (Math.abs(dy) > Math.abs(dx)) return false;
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
if(move(dx))
|
||||
{
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean move(float dx)
|
||||
{
|
||||
int newDataOffset = dataOffset + (int) (dx / columnWidth);
|
||||
newDataOffset = Math.max(0, Math.min(streaks.size() - nColumns, newDataOffset));
|
||||
|
||||
if (newDataOffset != dataOffset)
|
||||
{
|
||||
dataOffset = newDataOffset;
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class RingView extends View
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(size, size + (int) (2*lineHeight));
|
||||
setMeasuredDimension(size, size + (int) (2 * lineHeight));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,9 +75,10 @@ public class RingView extends View
|
||||
pRing.setColor(Color.GRAY);
|
||||
pRing.setTextSize(size * 0.2f);
|
||||
lineHeight = pRing.getFontSpacing();
|
||||
canvas.drawText(String.format("%.0f%%", perc * 100), r.centerX(), r.centerY()+lineHeight/3, pRing);
|
||||
canvas.drawText(String.format("%.0f%%", perc * 100), r.centerX(),
|
||||
r.centerY() + lineHeight / 3, pRing);
|
||||
|
||||
pRing.setTextSize(size * 0.15f);
|
||||
canvas.drawText(label, size/2, size + lineHeight * 1.2f, pRing);
|
||||
canvas.drawText(label, size / 2, size + lineHeight * 1.2f, pRing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Alinson Santos Xavier
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.view.MotionEventCompat;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
public abstract class ScrollableDataView extends View
|
||||
{
|
||||
|
||||
protected int dataOffset;
|
||||
protected int nColumns;
|
||||
protected int columnWidth, columnHeight;
|
||||
protected int headerHeight, footerHeight;
|
||||
|
||||
private float prevX, prevY;
|
||||
|
||||
public ScrollableDataView(Context context)
|
||||
{
|
||||
super(context);
|
||||
}
|
||||
|
||||
protected abstract void fetchData();
|
||||
|
||||
protected boolean move(float dx)
|
||||
{
|
||||
int newDataOffset = dataOffset + (int) (dx / columnWidth);
|
||||
newDataOffset = Math.max(0, newDataOffset);
|
||||
|
||||
if (newDataOffset != dataOffset)
|
||||
{
|
||||
dataOffset = newDataOffset;
|
||||
fetchData();
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event)
|
||||
{
|
||||
int action = event.getAction();
|
||||
|
||||
int pointerIndex = MotionEventCompat.getActionIndex(event);
|
||||
final float x = MotionEventCompat.getX(event, pointerIndex);
|
||||
final float y = MotionEventCompat.getY(event, pointerIndex);
|
||||
|
||||
if (action == MotionEvent.ACTION_DOWN)
|
||||
{
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
|
||||
if (action == MotionEvent.ACTION_MOVE)
|
||||
{
|
||||
float dx = x - prevX;
|
||||
float dy = y - prevY;
|
||||
|
||||
if (Math.abs(dy) > Math.abs(dx)) return false;
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
if (move(dx))
|
||||
{
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||
{
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredWidth(), columnHeight + headerHeight + footerHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh)
|
||||
{
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
nColumns = w / columnWidth;
|
||||
fetchData();
|
||||
}
|
||||
}
|
||||
BIN
app/src/main/res/drawable-hdpi/ic_action_archive_dark.png
Normal file
|
After Width: | Height: | Size: 338 B |
BIN
app/src/main/res/drawable-hdpi/ic_action_archive_light.png
Normal file
|
After Width: | Height: | Size: 393 B |
BIN
app/src/main/res/drawable-hdpi/ic_action_color_dark.png
Normal file
|
After Width: | Height: | Size: 492 B |
BIN
app/src/main/res/drawable-hdpi/ic_action_color_light.png
Normal file
|
After Width: | Height: | Size: 563 B |
BIN
app/src/main/res/drawable-hdpi/ic_action_edit_light.png
Normal file
|
After Width: | Height: | Size: 822 B |
BIN
app/src/main/res/drawable-hdpi/ic_action_unarchive_dark.png
Normal file
|
After Width: | Height: | Size: 337 B |
BIN
app/src/main/res/drawable-hdpi/ic_action_unarchive_light.png
Normal file
|
After Width: | Height: | Size: 410 B |
BIN
app/src/main/res/drawable-mdpi/ic_action_archive_dark.png
Normal file
|
After Width: | Height: | Size: 214 B |
BIN
app/src/main/res/drawable-mdpi/ic_action_archive_light.png
Normal file
|
After Width: | Height: | Size: 244 B |
BIN
app/src/main/res/drawable-mdpi/ic_action_color_dark.png
Normal file
|
After Width: | Height: | Size: 289 B |
BIN
app/src/main/res/drawable-mdpi/ic_action_color_light.png
Normal file
|
After Width: | Height: | Size: 319 B |
BIN
app/src/main/res/drawable-mdpi/ic_action_edit_light.png
Normal file
|
After Width: | Height: | Size: 554 B |
BIN
app/src/main/res/drawable-mdpi/ic_action_unarchive_dark.png
Normal file
|
After Width: | Height: | Size: 207 B |
BIN
app/src/main/res/drawable-mdpi/ic_action_unarchive_light.png
Normal file
|
After Width: | Height: | Size: 243 B |
BIN
app/src/main/res/drawable-xhdpi/ic_action_archive_dark.png
Normal file
|
After Width: | Height: | Size: 338 B |
BIN
app/src/main/res/drawable-xhdpi/ic_action_archive_light.png
Normal file
|
After Width: | Height: | Size: 390 B |
BIN
app/src/main/res/drawable-xhdpi/ic_action_color_dark.png
Normal file
|
After Width: | Height: | Size: 541 B |
BIN
app/src/main/res/drawable-xhdpi/ic_action_color_light.png
Normal file
|
After Width: | Height: | Size: 602 B |
BIN
app/src/main/res/drawable-xhdpi/ic_action_edit_light.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_action_unarchive_dark.png
Normal file
|
After Width: | Height: | Size: 337 B |
BIN
app/src/main/res/drawable-xhdpi/ic_action_unarchive_light.png
Normal file
|
After Width: | Height: | Size: 381 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_action_archive_dark.png
Normal file
|
After Width: | Height: | Size: 593 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_action_archive_light.png
Normal file
|
After Width: | Height: | Size: 674 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_action_color_dark.png
Normal file
|
After Width: | Height: | Size: 922 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_action_color_light.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_action_edit_light.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_action_unarchive_dark.png
Normal file
|
After Width: | Height: | Size: 575 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_action_unarchive_light.png
Normal file
|
After Width: | Height: | Size: 683 B |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
@@ -1,6 +0,0 @@
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/grey"> <!-- ripple color -->
|
||||
|
||||
<item android:drawable="@color/white"/> <!-- normal color -->
|
||||
|
||||
</ripple>
|
||||
8
app/src/main/res/drawable/ripple_transparent.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:colorControlHighlight">
|
||||
|
||||
<item android:id="@android:id/mask">
|
||||
<color android:color="@android:color/white" />
|
||||
</item>
|
||||
|
||||
</ripple>
|
||||
5
app/src/main/res/drawable/ripple_white.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:colorControlHighlight">
|
||||
|
||||
<item android:drawable="@color/white" />
|
||||
</ripple>
|
||||
5
app/src/main/res/drawable/selected_box.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
|
||||
<solid android:color="@color/grey_100" />
|
||||
<stroke android:width="2dip" android:color="@color/grey_500"/>
|
||||
</shape>
|
||||
@@ -1,7 +1,7 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/container"
|
||||
tools:context="org.isoron.uhabits.dialogs.EditHabitFragment"
|
||||
tools:context="org.isoron.uhabits.fragments.EditHabitFragment"
|
||||
tools:ignore="MergeRootFrame"
|
||||
style="@style/dialogForm">
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
|
||||
<EditText
|
||||
android:id="@+id/input_name"
|
||||
android:hint="Name"
|
||||
android:hint="@string/name"
|
||||
style="@style/dialogFormInput">
|
||||
|
||||
<requestFocus />
|
||||
</EditText>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_pick_color"
|
||||
android:src="@drawable/ic_action_pick_color"
|
||||
android:id="@+id/buttonPickColor"
|
||||
android:src="@drawable/ic_action_color_light"
|
||||
style="@style/dialogFormInputColor" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -72,7 +72,12 @@
|
||||
android:text="@string/reminder" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/input_reminder_time"
|
||||
android:id="@+id/inputReminderTime"
|
||||
style="@style/dialogFormTimePicker" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/inputReminderDays"
|
||||
android:text="Any weekday"
|
||||
style="@style/dialogFormTimePicker" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<fragment
|
||||
android:id="@+id/fragment1"
|
||||
android:name="org.isoron.uhabits.dialogs.ListHabitsFragment"
|
||||
android:name="org.isoron.uhabits.fragments.ListHabitsFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:layout="@layout/list_habits_fragment" />
|
||||
|
||||
@@ -9,12 +9,9 @@
|
||||
android:id="@+id/listView"
|
||||
style="@style/habitsListStyle"
|
||||
dslv:drag_enabled="true"
|
||||
dslv:drag_handle_id="@drawable/habits_header_check"
|
||||
dslv:drag_start_mode="onMove"
|
||||
dslv:float_alpha="0.5"
|
||||
dslv:drag_start_mode="onLongPress"
|
||||
dslv:sort_enabled="true"
|
||||
dslv:track_drag_sort="false"
|
||||
dslv:use_default_controller="true"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
@@ -60,4 +57,32 @@
|
||||
android:layout_marginTop="37dp"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llHint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/indigo_500"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:orientation="vertical"
|
||||
android:animateLayoutChanges="true"
|
||||
android:visibility="invisible"
|
||||
style="@style/cardStyle">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/hint_title"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/white"
|
||||
android:layout_weight="5"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hintContent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:layout_weight="5"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<fragment
|
||||
android:id="@+id/fragment2"
|
||||
android:name="org.isoron.uhabits.dialogs.ShowHabitFragment"
|
||||
android:name="org.isoron.uhabits.fragments.ShowHabitFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
|
||||
29
app/src/main/res/menu-v21/list_habits_context.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_edit_habit"
|
||||
android:title="@string/edit"
|
||||
android:icon="@drawable/ic_action_edit_dark"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_color"
|
||||
android:title="@string/color_picker_default_title"
|
||||
android:icon="@drawable/ic_action_color_dark"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_archive_habit"
|
||||
android:title="@string/archive"
|
||||
android:icon="@drawable/ic_action_archive_dark" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_unarchive_habit"
|
||||
android:title="@string/unarchive"
|
||||
android:icon="@drawable/ic_action_unarchive_dark"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:title="@string/delete"
|
||||
android:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
@@ -1,19 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_edit_habit"
|
||||
android:title="@string/edit">
|
||||
</item>
|
||||
android:title="@string/edit"
|
||||
android:icon="@drawable/ic_action_edit_light"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_color"
|
||||
android:title="@string/color_picker_default_title"
|
||||
android:icon="@drawable/ic_action_color_light"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_archive_habit"
|
||||
android:title="@string/archive">
|
||||
</item>
|
||||
android:title="@string/archive"
|
||||
android:icon="@drawable/ic_action_archive_light" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_unarchive_habit"
|
||||
android:title="@string/unarchive">
|
||||
</item>
|
||||
android:title="@string/unarchive"
|
||||
android:icon="@drawable/ic_action_unarchive_light"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:title="@string/delete" />
|
||||
</menu>
|
||||
@@ -3,7 +3,7 @@
|
||||
tools:context="org.isoron.uhabits.MainActivity" >
|
||||
|
||||
<item android:id="@+id/action_show_archived"
|
||||
android:title="Show archived"
|
||||
android:title="@string/show_archived"
|
||||
android:enabled="true"
|
||||
android:checkable="true" />
|
||||
|
||||
|
||||
103
app/src/main/res/values-pt/strings.xml
Normal file
@@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2016 Alinson Santos Xavier
|
||||
~
|
||||
~ This program 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.
|
||||
~
|
||||
~ This program 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/>.
|
||||
~
|
||||
~
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_name">Hábitos</string>
|
||||
<string name="action_settings">Configurações</string>
|
||||
<string name="add_habit">Adicionar hábito</string>
|
||||
<string name="archive">Arquivar</string>
|
||||
<string name="check">Marcar</string>
|
||||
<string name="clear">Limpar</string>
|
||||
<string name="clear_label">Limpar</string>
|
||||
<string name="color_picker_default_title">Mudar cor</string>
|
||||
<string name="create_habit">Criar hábito</string>
|
||||
<string name="days">dias</string>
|
||||
<string name="delete">Deletar</string>
|
||||
<string name="description">Descrição</string>
|
||||
<string name="description_hint">Pergunta (por ex., \"você meditou hoje?\")</string>
|
||||
<string name="discard">Cancelar</string>
|
||||
<string name="edit">Editar</string>
|
||||
<string name="edit_habit">Editar hábito</string>
|
||||
<string name="habit_strength">Estabilidade</string>
|
||||
<string name="history">Histórico</string>
|
||||
<string name="interval_15_minutes">15 minutos</string>
|
||||
<string name="interval_1_hour">1 hora</string>
|
||||
<string name="interval_2_hour">2 horas</string>
|
||||
<string name="interval_30_minutes">30 minutos</string>
|
||||
<string name="interval_4_hour">4 horas</string>
|
||||
<string name="interval_8_hour">8 horas</string>
|
||||
<string name="intro_title_1">Bem vindo</string>
|
||||
<string name="long_press_to_toggle">Sustente por um segundo para marcar ou desmarcar</string>
|
||||
<string name="main_activity_title">Hábitos</string>
|
||||
<string name="overview">Visão geral</string>
|
||||
<string name="reminder">Lembrete</string>
|
||||
<string name="reminder_off">Desligado</string>
|
||||
<string name="repeat">Repetir</string>
|
||||
<string name="save">Salvar</string>
|
||||
<string name="select_day">Selecionar mês e dia</string>
|
||||
<string name="select_hours">Selecionar horas</string>
|
||||
<string name="select_minutes">Selecionar minutos</string>
|
||||
<string name="select_year">Selecionar ano</string>
|
||||
<string name="snooze">Mais tarde</string>
|
||||
<string name="streaks">Correntes</string>
|
||||
<string name="no_habits_found">Você não tem nenhum hábito ativo</string>
|
||||
<string name="time_separator"></string>
|
||||
<string name="toast_habit_archived">Hábitos arquivados.</string>
|
||||
<string name="toast_habit_changed">Hábito modificado.</string>
|
||||
<string name="toast_habit_changed_back">Hábito restaurado.</string>
|
||||
<string name="toast_habit_created">Hábito criado.</string>
|
||||
<string name="toast_habit_deleted">Hábito deletado.</string>
|
||||
<string name="toast_habit_unarchived">Hábitos restaurados.</string>
|
||||
<string name="toast_nothing_to_redo">Nada para refazer.</string>
|
||||
<string name="toast_nothing_to_undo">Nada para desfazer.</string>
|
||||
<string name="toast_repetition_toggled">Marcado.</string>
|
||||
<string name="unarchive">Desarquivar</string>
|
||||
<string name="validation_at_most_one_rep_per_day">Você pode ter no máximo uma repetição por dia.</string>
|
||||
<string name="validation_name_should_not_be_blank">Nome não pode ficar em branco.</string>
|
||||
<string name="validation_number_should_be_positive">Número precisa ser positivo.</string>
|
||||
<string name="done_label">Pronto</string>
|
||||
<string name="pref_view_source_code">Ver código-fonte no GitHub</string>
|
||||
<string name="pref_view_app_introduction">Assistir introdução ao aplicativo</string>
|
||||
<string name="pref_toggle_title">Marcar repetições com um toque curto</string>
|
||||
<string name="pref_toggle_description">Mais conveniente, mas pode causar marcações acidentais</string>
|
||||
<string name="pref_snooze_interval_title">Duração do \"mais tarde\" nos lembretes</string>
|
||||
<string name="pref_send_feedback">Mandar sugestões para o desenvolvedor</string>
|
||||
<string name="pref_rate_this_app">Avaliar esse app no Google Play</string>
|
||||
<string name="links">Links</string>
|
||||
<string name="behavior">Interação</string>
|
||||
<string name="name">Nome</string>
|
||||
<string name="times_every">vezes em</string>
|
||||
<string name="show_archived">Mostrar hábitos arquivados</string>
|
||||
<string name="snooze_interval">Duração do \"mais tarde\"</string>
|
||||
<string name="settings">Configurações</string>
|
||||
<string name="intro_description_1">Loop é um aplicativo que te ajuda a criar e manter bons hábitos.</string>
|
||||
<string name="intro_title_2">Adicione alguns hábitos</string>
|
||||
<string name="intro_description_2">Todo dia, depois de praticar o seu hábito, marque no aplicativo.</string>
|
||||
<string name="intro_description_3">Hábitos praticados regularmente por um longo período recebem uma estrela.</string>
|
||||
<string name="intro_title_3">Continue praticando</string>
|
||||
<string name="intro_title_4">Acompanhe o seu progresso</string>
|
||||
<string name="intro_description_4">Veja como seus hábitos estão progredindo através de diagramas.</string>
|
||||
<string name="hint_title">Dica</string>
|
||||
<string name="hint_drag">Para mudar a ordem dos hábitos, aperte no nome do hábito, sustente e arraste.</string>
|
||||
<string name="hint_landscape">Para ver mais dias, coloque seu aparelho em modo paisagem.</string>
|
||||
<string name="delete_habits">Deletar hábitos</string>
|
||||
<string name="toast_habit_restored">Hábitos restaurados.</string>
|
||||
<string name="delete_habits_message">Os hábitos escolhidos serão deletados permanentemente. Esta ação não pode ser desfeita.</string>
|
||||
</resources>
|
||||
@@ -7,6 +7,13 @@
|
||||
</style>
|
||||
|
||||
<style name="habitsListCheckStyle" parent="habitsListCheckBasicStyle">
|
||||
<item name="android:background">@drawable/ripple_background</item>
|
||||
<item name="android:background">@drawable/ripple_transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="habitsListItemInnerPanelStyle" parent="cardStyle">
|
||||
<item name="android:orientation">horizontal</item>
|
||||
<item name="android:padding">3dp</item>
|
||||
<item name="android:background">@drawable/ripple_white</item>
|
||||
<item name="android:elevation">1dp</item>
|
||||
</style>
|
||||
</resources>
|
||||
91
app/src/main/res/values-zh/strings.xml
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<resources>
|
||||
<string name="app_name">循环习惯记录</string>
|
||||
<string name="main_activity_title">习惯</string>
|
||||
<string name="action_settings">设置</string>
|
||||
<string name="edit">编辑</string>
|
||||
<string name="delete">删除</string>
|
||||
<string name="archive">存档</string>
|
||||
<string name="unarchive">取消存档</string>
|
||||
<string name="add_habit">添加新习惯</string>
|
||||
<string name="color_picker_default_title">选择颜色</string>
|
||||
|
||||
<string name="toast_habit_created">习惯建成.</string>
|
||||
<string name="toast_habit_deleted">习惯删除.</string>
|
||||
<string name="toast_nothing_to_undo">Nothing to undo.</string>
|
||||
<string name="toast_nothing_to_redo">Nothing to redo.</string>
|
||||
<string name="toast_habit_changed">习惯修改了.</string>
|
||||
<string name="toast_habit_changed_back">取消了修改.</string>
|
||||
<string name="toast_repetition_toggled">Repetition toggled.</string>
|
||||
<string name="toast_habit_archived">习惯存档成功.</string>
|
||||
<string name="toast_habit_unarchived">习惯取消存档成功.</string>
|
||||
|
||||
<!-- Date and time picker -->
|
||||
<string name="done_label">完陈</string>
|
||||
<string name="clear_label">取消</string>
|
||||
<string name="select_hours">选择小时</string>
|
||||
<string name="select_minutes">选择分钟</string>
|
||||
<string name="year_picker_description">Year list</string>
|
||||
<string name="select_day">Select month and day</string>
|
||||
<string name="select_year">Select year</string>
|
||||
|
||||
<string name="title_activity_show_habit" />
|
||||
<string name="overview">总览</string>
|
||||
<string name="habit_strength">习惯强度</string>
|
||||
<string name="history">历史</string>
|
||||
<string name="clear">取消</string>
|
||||
<string name="description">描叙</string>
|
||||
<string name="description_hint">提醒问题(你xxx了吗)</string>
|
||||
<string name="repeat">重复</string>
|
||||
<string name="times_every">次每</string>
|
||||
<string name="days">天</string>
|
||||
<string name="reminder">提醒</string>
|
||||
<string name="discard">丢弃</string>
|
||||
<string name="save">保存</string>
|
||||
<string name="streaks">链条</string>
|
||||
<string name="no_habits_found">你没有习惯</string>
|
||||
<string name="long_press_to_toggle">长按 标记/取消标记</string>
|
||||
<string name="reminder_off">关</string>
|
||||
<string name="validation_name_should_not_be_blank">名字不能是空白.</string>
|
||||
<string name="validation_number_should_be_positive">数字必须大于零.</string>
|
||||
<string name="validation_at_most_one_rep_per_day">每天最多重复一次</string>
|
||||
<string name="create_habit">新建习惯</string>
|
||||
<string name="edit_habit">编辑习惯</string>
|
||||
<string name="check">标记</string>
|
||||
<string name="snooze">以后再说</string>
|
||||
|
||||
<!-- App introduction -->
|
||||
<string name="intro_title_1">欢迎</string>
|
||||
<string name="intro_description_1">循环习惯记录器帮助你建立和维持好习惯.</string>
|
||||
|
||||
<string name="intro_title_2">建立一些新习惯</string>
|
||||
<string name="intro_description_2">每天,你完成一项习惯后,在应用上做一个标记</string>
|
||||
|
||||
<string name="intro_title_3">保持习惯</string>
|
||||
<string name="intro_description_3">你可以得到一个完整的星星在连续性的完成习惯达到一定时间</string>
|
||||
|
||||
<string name="intro_title_4">记录你的进步</string>
|
||||
<string name="intro_description_4">详细的图形显示你的进步</string>
|
||||
|
||||
<string name="interval_15_minutes">15分钟</string>
|
||||
<string name="interval_30_minutes">30分钟</string>
|
||||
<string name="interval_1_hour">1小时</string>
|
||||
<string name="interval_2_hour">2小时</string>
|
||||
<string name="interval_4_hour">4小时</string>
|
||||
<string name="interval_8_hour">8小时</string>
|
||||
<string name="pref_toggle_title">短时间按来记录习惯</string>
|
||||
<string name="pref_toggle_description">更加方便,但有可能造成意外记录</string>
|
||||
<string name="pref_snooze_interval_title">提醒延迟间隔</string>
|
||||
<string name="pref_rate_this_app">评价这个应用在Play商店</string>
|
||||
<string name="pref_send_feedback">发送反馈给开发者</string>
|
||||
<string name="pref_view_source_code">看源代码在GitHub</string>
|
||||
<string name="pref_view_app_introduction">阅读应用介绍</string>
|
||||
|
||||
<string name="links">链接</string>
|
||||
<string name="behavior">功能</string>
|
||||
<string name="name">名字</string>
|
||||
<string name="show_archived">显示存档的习惯</string>
|
||||
<string name="settings">设置</string>
|
||||
<string name="snooze_interval">延迟时间间隔</string>
|
||||
</resources>
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!--<declare-styleable name="DragSortListView">-->
|
||||
<!--<attr name="collapsed_height" format="dimension"/>-->
|
||||
<!--<attr name="drag_scroll_start" format="float"/>-->
|
||||
<!--<attr name="max_drag_scroll_speed" format="float"/>-->
|
||||
<!--<attr name="float_background_color" format="color"/>-->
|
||||
<!--<attr name="remove_mode">-->
|
||||
<!--<enum name="clickRemove" value="0"/>-->
|
||||
<!--<enum name="flingRemove" value="1"/>-->
|
||||
<!--</attr>-->
|
||||
<!--<attr name="track_drag_sort" format="boolean"/>-->
|
||||
<!--<attr name="float_alpha" format="float"/>-->
|
||||
<!--<attr name="slide_shuffle_speed" format="float"/>-->
|
||||
<!--<attr name="remove_animation_duration" format="integer"/>-->
|
||||
<!--<attr name="drop_animation_duration" format="integer"/>-->
|
||||
<!--<attr name="drag_enabled" format="boolean"/>-->
|
||||
<!--<attr name="sort_enabled" format="boolean"/>-->
|
||||
<!--<attr name="remove_enabled" format="boolean"/>-->
|
||||
<!--<attr name="drag_start_mode">-->
|
||||
<!--<enum name="onDown" value="0"/>-->
|
||||
<!--<enum name="onMove" value="1"/>-->
|
||||
<!--<enum name="onLongPress" value="2"/>-->
|
||||
<!--</attr>-->
|
||||
<!--<attr name="drag_handle_id" format="integer"/>-->
|
||||
<!--<attr name="fling_handle_id" format="integer"/>-->
|
||||
<!--<attr name="click_remove_id" format="integer"/>-->
|
||||
<!--<attr name="use_default_controller" format="boolean"/>-->
|
||||
<!--</declare-styleable>-->
|
||||
</resources>
|
||||
@@ -53,4 +53,278 @@
|
||||
<color name="done_text_color_dark_normal">#ffffff</color>
|
||||
<color name="done_text_color_dark_disabled">#888888</color>
|
||||
<color name="done_disabled_dark">#bfbfbf</color>
|
||||
|
||||
<!-- Material design color palette -->
|
||||
<color name="red_50">#FFEBEE</color>
|
||||
<color name="red_100">#FFCDD2</color>
|
||||
<color name="red_200">#EF9A9A</color>
|
||||
<color name="red_300">#E57373</color>
|
||||
<color name="red_400">#EF5350</color>
|
||||
<color name="red_500">#F44336</color>
|
||||
<color name="red_600">#E53935</color>
|
||||
<color name="red_700">#D32F2F</color>
|
||||
<color name="red_800">#C62828</color>
|
||||
<color name="red_900">#B71C1C</color>
|
||||
<color name="red_A100">#FF8A80</color>
|
||||
<color name="red_A200">#FF5252</color>
|
||||
<color name="red_A400">#FF1744</color>
|
||||
<color name="red_A700">#D50000</color>
|
||||
|
||||
<color name="deep_purple_50">#EDE7F6</color>
|
||||
<color name="deep_purple_100">#D1C4E9</color>
|
||||
<color name="deep_purple_200">#B39DDB</color>
|
||||
<color name="deep_purple_300">#9575CD</color>
|
||||
<color name="deep_purple_400">#7E57C2</color>
|
||||
<color name="deep_purple_500">#673AB7</color>
|
||||
<color name="deep_purple_600">#5E35B1</color>
|
||||
<color name="deep_purple_700">#512DA8</color>
|
||||
<color name="deep_purple_800">#4527A0</color>
|
||||
<color name="deep_purple_900">#311B92</color>
|
||||
<color name="deep_purple_A100">#B388FF</color>
|
||||
<color name="deep_purple_A200">#7C4DFF</color>
|
||||
<color name="deep_purple_A400">#651FFF</color>
|
||||
<color name="deep_purple_A700">#6200EA</color>
|
||||
|
||||
<color name="light_blue_50">#E1F5FE</color>
|
||||
<color name="light_blue_100">#B3E5FC</color>
|
||||
<color name="light_blue_200">#81D4FA</color>
|
||||
<color name="light_blue_300">#4FC3F7</color>
|
||||
<color name="light_blue_400">#29B6F6</color>
|
||||
<color name="light_blue_500">#03A9F4</color>
|
||||
<color name="light_blue_600">#039BE5</color>
|
||||
<color name="light_blue_700">#0288D1</color>
|
||||
<color name="light_blue_800">#0277BD</color>
|
||||
<color name="light_blue_900">#01579B</color>
|
||||
<color name="light_blue_A100">#80D8FF</color>
|
||||
<color name="light_blue_A200">#40C4FF</color>
|
||||
<color name="light_blue_A400">#00B0FF</color>
|
||||
<color name="light_blue_A700">#0091EA</color>
|
||||
|
||||
<color name="green_50">#E8F5E9</color>
|
||||
<color name="green_100">#C8E6C9</color>
|
||||
<color name="green_200">#A5D6A7</color>
|
||||
<color name="green_300">#81C784</color>
|
||||
<color name="green_400">#66BB6A</color>
|
||||
<color name="green_500">#4CAF50</color>
|
||||
<color name="green_600">#43A047</color>
|
||||
<color name="green_700">#388E3C</color>
|
||||
<color name="green_800">#2E7D32</color>
|
||||
<color name="green_900">#1B5E20</color>
|
||||
<color name="green_A100">#B9F6CA</color>
|
||||
<color name="green_A200">#69F0AE</color>
|
||||
<color name="green_A400">#00E676</color>
|
||||
<color name="green_A700">#00C853</color>
|
||||
|
||||
<color name="yellow_50">#FFFDE7</color>
|
||||
<color name="yellow_100">#FFF9C4</color>
|
||||
<color name="yellow_200">#FFF59D</color>
|
||||
<color name="yellow_300">#FFF176</color>
|
||||
<color name="yellow_400">#FFEE58</color>
|
||||
<color name="yellow_500">#FFEB3B</color>
|
||||
<color name="yellow_600">#FDD835</color>
|
||||
<color name="yellow_700">#FBC02D</color>
|
||||
<color name="yellow_800">#F9A825</color>
|
||||
<color name="yellow_900">#F57F17</color>
|
||||
<color name="yellow_A100">#FFFF8D</color>
|
||||
<color name="yellow_A200">#FFFF00</color>
|
||||
<color name="yellow_A400">#FFEA00</color>
|
||||
<color name="yellow_A700">#FFD600</color>
|
||||
|
||||
<color name="deep_orange_50">#FBE9E7</color>
|
||||
<color name="deep_orange_100">#FFCCBC</color>
|
||||
<color name="deep_orange_200">#FFAB91</color>
|
||||
<color name="deep_orange_300">#FF8A65</color>
|
||||
<color name="deep_orange_400">#FF7043</color>
|
||||
<color name="deep_orange_500">#FF5722</color>
|
||||
<color name="deep_orange_600">#F4511E</color>
|
||||
<color name="deep_orange_700">#E64A19</color>
|
||||
<color name="deep_orange_800">#D84315</color>
|
||||
<color name="deep_orange_900">#BF360C</color>
|
||||
<color name="deep_orange_A100">#FF9E80</color>
|
||||
<color name="deep_orange_A200">#FF6E40</color>
|
||||
<color name="deep_orange_A400">#FF3D00</color>
|
||||
<color name="deep_orange_A700">#DD2C00</color>
|
||||
|
||||
<color name="blue_grey_50">#ECEFF1</color>
|
||||
<color name="blue_grey_100">#CFD8DC</color>
|
||||
<color name="blue_grey_200">#B0BEC5</color>
|
||||
<color name="blue_grey_300">#90A4AE</color>
|
||||
<color name="blue_grey_400">#78909C</color>
|
||||
<color name="blue_grey_500">#607D8B</color>
|
||||
<color name="blue_grey_600">#546E7A</color>
|
||||
<color name="blue_grey_700">#455A64</color>
|
||||
<color name="blue_grey_800">#37474F</color>
|
||||
<color name="blue_grey_900">#263238</color>
|
||||
|
||||
<color name="pink_50">#FCE4EC</color>
|
||||
<color name="pink_100">#F8BBD0</color>
|
||||
<color name="pink_200">#F48FB1</color>
|
||||
<color name="pink_300">#F06292</color>
|
||||
<color name="pink_400">#EC407A</color>
|
||||
<color name="pink_500">#E91E63</color>
|
||||
<color name="pink_600">#D81B60</color>
|
||||
<color name="pink_700">#C2185B</color>
|
||||
<color name="pink_800">#AD1457</color>
|
||||
<color name="pink_900">#880E4F</color>
|
||||
<color name="pink_A100">#FF80AB</color>
|
||||
<color name="pink_A200">#FF4081</color>
|
||||
<color name="pink_A400">#F50057</color>
|
||||
<color name="pink_A700">#C51162</color>
|
||||
|
||||
<color name="indigo_50">#E8EAF6</color>
|
||||
<color name="indigo_100">#C5CAE9</color>
|
||||
<color name="indigo_200">#9FA8DA</color>
|
||||
<color name="indigo_300">#7986CB</color>
|
||||
<color name="indigo_400">#5C6BC0</color>
|
||||
<color name="indigo_500">#3F51B5</color>
|
||||
<color name="indigo_600">#3949AB</color>
|
||||
<color name="indigo_700">#303F9F</color>
|
||||
<color name="indigo_800">#283593</color>
|
||||
<color name="indigo_900">#1A237E</color>
|
||||
<color name="indigo_A100">#8C9EFF</color>
|
||||
<color name="indigo_A200">#536DFE</color>
|
||||
<color name="indigo_A400">#3D5AFE</color>
|
||||
<color name="indigo_A700">#304FFE</color>
|
||||
|
||||
<color name="cyan_50">#E0F7FA</color>
|
||||
<color name="cyan_100">#B2EBF2</color>
|
||||
<color name="cyan_200">#80DEEA</color>
|
||||
<color name="cyan_300">#4DD0E1</color>
|
||||
<color name="cyan_400">#26C6DA</color>
|
||||
<color name="cyan_500">#00BCD4</color>
|
||||
<color name="cyan_600">#00ACC1</color>
|
||||
<color name="cyan_700">#0097A7</color>
|
||||
<color name="cyan_800">#00838F</color>
|
||||
<color name="cyan_900">#006064</color>
|
||||
<color name="cyan_A100">#84FFFF</color>
|
||||
<color name="cyan_A200">#18FFFF</color>
|
||||
<color name="cyan_A400">#00E5FF</color>
|
||||
<color name="cyan_A700">#00B8D4</color>
|
||||
|
||||
<color name="light_green_50">#F1F8E9</color>
|
||||
<color name="light_green_100">#DCEDC8</color>
|
||||
<color name="light_green_200">#C5E1A5</color>
|
||||
<color name="light_green_300">#AED581</color>
|
||||
<color name="light_green_400">#9CCC65</color>
|
||||
<color name="light_green_500">#8BC34A</color>
|
||||
<color name="light_green_600">#7CB342</color>
|
||||
<color name="light_green_700">#689F38</color>
|
||||
<color name="light_green_800">#558B2F</color>
|
||||
<color name="light_green_900">#33691E</color>
|
||||
<color name="light_green_A100">#CCFF90</color>
|
||||
<color name="light_green_A200">#B2FF59</color>
|
||||
<color name="light_green_A400">#76FF03</color>
|
||||
<color name="light_green_A700">#64DD17</color>
|
||||
|
||||
<color name="amber_50">#FFF8E1</color>
|
||||
<color name="amber_100">#FFECB3</color>
|
||||
<color name="amber_200">#FFE082</color>
|
||||
<color name="amber_300">#FFD54F</color>
|
||||
<color name="amber_400">#FFCA28</color>
|
||||
<color name="amber_500">#FFC107</color>
|
||||
<color name="amber_600">#FFB300</color>
|
||||
<color name="amber_700">#FFA000</color>
|
||||
<color name="amber_800">#FF8F00</color>
|
||||
<color name="amber_900">#FF6F00</color>
|
||||
<color name="amber_A100">#FFE57F</color>
|
||||
<color name="amber_A200">#FFD740</color>
|
||||
<color name="amber_A400">#FFC400</color>
|
||||
<color name="amber_A700">#FFAB00</color>
|
||||
|
||||
<color name="brown_50">#EFEBE9</color>
|
||||
<color name="brown_100">#D7CCC8</color>
|
||||
<color name="brown_200">#BCAAA4</color>
|
||||
<color name="brown_300">#A1887F</color>
|
||||
<color name="brown_400">#8D6E63</color>
|
||||
<color name="brown_500">#795548</color>
|
||||
<color name="brown_600">#6D4C41</color>
|
||||
<color name="brown_700">#5D4037</color>
|
||||
<color name="brown_800">#4E342E</color>
|
||||
<color name="brown_900">#3E2723</color>
|
||||
|
||||
<color name="purple_50">#F3E5F5</color>
|
||||
<color name="purple_100">#E1BEE7</color>
|
||||
<color name="purple_200">#CE93D8</color>
|
||||
<color name="purple_300">#BA68C8</color>
|
||||
<color name="purple_400">#AB47BC</color>
|
||||
<color name="purple_500">#9C27B0</color>
|
||||
<color name="purple_600">#8E24AA</color>
|
||||
<color name="purple_700">#7B1FA2</color>
|
||||
<color name="purple_800">#6A1B9A</color>
|
||||
<color name="purple_900">#4A148C</color>
|
||||
<color name="purple_A100">#EA80FC</color>
|
||||
<color name="purple_A200">#E040FB</color>
|
||||
<color name="purple_A400">#D500F9</color>
|
||||
<color name="purple_A700">#AA00FF</color>
|
||||
|
||||
<color name="blue_50">#E3F2FD</color>
|
||||
<color name="blue_100">#BBDEFB</color>
|
||||
<color name="blue_200">#90CAF9</color>
|
||||
<color name="blue_300">#64B5F6</color>
|
||||
<color name="blue_400">#42A5F5</color>
|
||||
<color name="blue_500">#2196F3</color>
|
||||
<color name="blue_600">#1E88E5</color>
|
||||
<color name="blue_700">#1976D2</color>
|
||||
<color name="blue_800">#1565C0</color>
|
||||
<color name="blue_900">#0D47A1</color>
|
||||
<color name="blue_A100">#82B1FF</color>
|
||||
<color name="blue_A200">#448AFF</color>
|
||||
<color name="blue_A400">#2979FF</color>
|
||||
<color name="blue_A700">#2962FF</color>
|
||||
|
||||
<color name="teal_50">#E0F2F1</color>
|
||||
<color name="teal_100">#B2DFDB</color>
|
||||
<color name="teal_200">#80CBC4</color>
|
||||
<color name="teal_300">#4DB6AC</color>
|
||||
<color name="teal_400">#26A69A</color>
|
||||
<color name="teal_500">#009688</color>
|
||||
<color name="teal_600">#00897B</color>
|
||||
<color name="teal_700">#00796B</color>
|
||||
<color name="teal_800">#00695C</color>
|
||||
<color name="teal_900">#004D40</color>
|
||||
<color name="teal_A100">#A7FFEB</color>
|
||||
<color name="teal_A200">#64FFDA</color>
|
||||
<color name="teal_A400">#1DE9B6</color>
|
||||
<color name="teal_A700">#00BFA5</color>
|
||||
|
||||
<color name="lime_50">#F9FBE7</color>
|
||||
<color name="lime_100">#F0F4C3</color>
|
||||
<color name="lime_200">#E6EE9C</color>
|
||||
<color name="lime_300">#DCE775</color>
|
||||
<color name="lime_400">#D4E157</color>
|
||||
<color name="lime_500">#CDDC39</color>
|
||||
<color name="lime_600">#C0CA33</color>
|
||||
<color name="lime_700">#AFB42B</color>
|
||||
<color name="lime_800">#9E9D24</color>
|
||||
<color name="lime_900">#827717</color>
|
||||
<color name="lime_A100">#F4FF81</color>
|
||||
<color name="lime_A200">#EEFF41</color>
|
||||
<color name="lime_A400">#C6FF00</color>
|
||||
<color name="lime_A700">#AEEA00</color>
|
||||
|
||||
<color name="orange_50">#FFF3E0</color>
|
||||
<color name="orange_100">#FFE0B2</color>
|
||||
<color name="orange_200">#FFCC80</color>
|
||||
<color name="orange_300">#FFB74D</color>
|
||||
<color name="orange_400">#FFA726</color>
|
||||
<color name="orange_500">#FF9800</color>
|
||||
<color name="orange_600">#FB8C00</color>
|
||||
<color name="orange_700">#F57C00</color>
|
||||
<color name="orange_800">#EF6C00</color>
|
||||
<color name="orange_900">#E65100</color>
|
||||
<color name="orange_A100">#FFD180</color>
|
||||
<color name="orange_A200">#FFAB40</color>
|
||||
<color name="orange_A400">#FF9100</color>
|
||||
<color name="orange_A700">#FF6D00</color>
|
||||
|
||||
<color name="grey_50">#FAFAFA</color>
|
||||
<color name="grey_100">#F5F5F5</color>
|
||||
<color name="grey_200">#EEEEEE</color>
|
||||
<color name="grey_300">#E0E0E0</color>
|
||||
<color name="grey_400">#BDBDBD</color>
|
||||
<color name="grey_500">#9E9E9E</color>
|
||||
<color name="grey_600">#757575</color>
|
||||
<color name="grey_700">#616161</color>
|
||||
<color name="grey_800">#424242</color>
|
||||
<color name="grey_900">#212121</color>
|
||||
</resources>
|
||||
@@ -5,12 +5,12 @@
|
||||
<dimen name="check_square_size">42dp</dimen>
|
||||
|
||||
<string-array name="snooze_interval_names">
|
||||
<item>15 minutes</item>
|
||||
<item>30 minutes</item>
|
||||
<item>1 hour</item>
|
||||
<item>2 hours</item>
|
||||
<item>4 hours</item>
|
||||
<item>8 hours</item>
|
||||
<item>@string/interval_15_minutes</item>
|
||||
<item>@string/interval_30_minutes</item>
|
||||
<item>@string/interval_1_hour</item>
|
||||
<item>@string/interval_2_hour</item>
|
||||
<item>@string/interval_4_hour</item>
|
||||
<item>@string/interval_8_hour</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="snooze_interval_values">
|
||||
@@ -22,5 +22,5 @@
|
||||
<item>480</item>
|
||||
</string-array>
|
||||
|
||||
<string name="snooze_interval_default">15 minutes</string>
|
||||
<string name="snooze_interval_default" translatable="false">15</string>
|
||||
</resources>
|
||||
@@ -1,15 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="circle_radius_multiplier" format="float" type="string">0.82</item>
|
||||
<item name="circle_radius_multiplier_24HourMode" format="float" type="string">0.85</item>
|
||||
<item name="selection_radius_multiplier" format="float" type="string">0.16</item>
|
||||
<item name="ampm_circle_radius_multiplier" format="float" type="string">0.19</item>
|
||||
<item name="numbers_radius_multiplier_normal" format="float" type="string">0.81</item>
|
||||
<item name="numbers_radius_multiplier_inner" format="float" type="string">0.60</item>
|
||||
<item name="numbers_radius_multiplier_outer" format="float" type="string">0.83</item>
|
||||
<item name="text_size_multiplier_normal" format="float" type="string">0.17</item>
|
||||
<item name="text_size_multiplier_inner" format="float" type="string">0.14</item>
|
||||
<item name="text_size_multiplier_outer" format="float" type="string">0.11</item>
|
||||
<item name="circle_radius_multiplier" format="float" type="string" translatable="false">0.82</item>
|
||||
<item name="circle_radius_multiplier_24HourMode" format="float" type="string" translatable="false">0.85</item>
|
||||
<item name="selection_radius_multiplier" format="float" type="string" translatable="false">0.16</item>
|
||||
<item name="ampm_circle_radius_multiplier" format="float" type="string" translatable="false">0.19</item>
|
||||
<item name="numbers_radius_multiplier_normal" format="float" type="string" translatable="false">0.81</item>
|
||||
<item name="numbers_radius_multiplier_inner" format="float" type="string" translatable="false">0.60</item>
|
||||
<item name="numbers_radius_multiplier_outer" format="float" type="string" translatable="false">0.83</item>
|
||||
<item name="text_size_multiplier_normal" format="float" type="string" translatable="false">0.17</item>
|
||||
<item name="text_size_multiplier_inner" format="float" type="string" translatable="false">0.14</item>
|
||||
<item name="text_size_multiplier_outer" format="float" type="string" translatable="false">0.11</item>
|
||||
|
||||
<dimen name="time_label_size">60sp</dimen>
|
||||
<dimen name="extra_time_label_margin">-30dp</dimen>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<declare-styleable name="DragSortListView">
|
||||
<attr name="collapsed_height" format="dimension" />
|
||||
<attr name="drag_scroll_start" format="float" />
|
||||
<attr name="max_drag_scroll_speed" format="float" />
|
||||
<attr name="float_background_color" format="color" />
|
||||
<attr name="remove_mode">
|
||||
<enum name="clickRemove" value="0" />
|
||||
<enum name="flingRemove" value="1" />
|
||||
</attr>
|
||||
<attr name="track_drag_sort" format="boolean"/>
|
||||
<attr name="float_alpha" format="float"/>
|
||||
<attr name="slide_shuffle_speed" format="float"/>
|
||||
<attr name="remove_animation_duration" format="integer"/>
|
||||
<attr name="drop_animation_duration" format="integer"/>
|
||||
<attr name="drag_enabled" format="boolean" />
|
||||
<attr name="sort_enabled" format="boolean" />
|
||||
<attr name="remove_enabled" format="boolean" />
|
||||
<attr name="drag_start_mode">
|
||||
<enum name="onDown" value="0" />
|
||||
<enum name="onMove" value="1" />
|
||||
<enum name="onLongPress" value="2"/>
|
||||
</attr>
|
||||
<attr name="drag_handle_id" format="integer" />
|
||||
<attr name="fling_handle_id" format="integer" />
|
||||
<attr name="click_remove_id" format="integer" />
|
||||
<attr name="use_default_controller" format="boolean" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -1,372 +1,372 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="fa_glass"></string>
|
||||
<string name="fa_music"></string>
|
||||
<string name="fa_search"></string>
|
||||
<string name="fa_envelope_o"></string>
|
||||
<string name="fa_heart"></string>
|
||||
<string name="fa_star"></string>
|
||||
<string name="fa_star_o"></string>
|
||||
<string name="fa_user"></string>
|
||||
<string name="fa_film"></string>
|
||||
<string name="fa_th_large"></string>
|
||||
<string name="fa_th"></string>
|
||||
<string name="fa_th_list"></string>
|
||||
<string name="fa_check"></string>
|
||||
<string name="fa_times"></string>
|
||||
<string name="fa_search_plus"></string>
|
||||
<string name="fa_search_minus"></string>
|
||||
<string name="fa_power_off"></string>
|
||||
<string name="fa_signal"></string>
|
||||
<string name="fa_cog"></string>
|
||||
<string name="fa_trash_o"></string>
|
||||
<string name="fa_home"></string>
|
||||
<string name="fa_file_o"></string>
|
||||
<string name="fa_clock_o"></string>
|
||||
<string name="fa_road"></string>
|
||||
<string name="fa_download"></string>
|
||||
<string name="fa_arrow_circle_o_down"></string>
|
||||
<string name="fa_arrow_circle_o_up"></string>
|
||||
<string name="fa_inbox"></string>
|
||||
<string name="fa_play_circle_o"></string>
|
||||
<string name="fa_repeat"></string>
|
||||
<string name="fa_refresh"></string>
|
||||
<string name="fa_list_alt"></string>
|
||||
<string name="fa_lock"></string>
|
||||
<string name="fa_flag"></string>
|
||||
<string name="fa_headphones"></string>
|
||||
<string name="fa_volume_off"></string>
|
||||
<string name="fa_volume_down"></string>
|
||||
<string name="fa_volume_up"></string>
|
||||
<string name="fa_qrcode"></string>
|
||||
<string name="fa_barcode"></string>
|
||||
<string name="fa_tag"></string>
|
||||
<string name="fa_tags"></string>
|
||||
<string name="fa_book"></string>
|
||||
<string name="fa_bookmark"></string>
|
||||
<string name="fa_print"></string>
|
||||
<string name="fa_camera"></string>
|
||||
<string name="fa_font"></string>
|
||||
<string name="fa_bold"></string>
|
||||
<string name="fa_italic"></string>
|
||||
<string name="fa_text_height"></string>
|
||||
<string name="fa_text_width"></string>
|
||||
<string name="fa_align_left"></string>
|
||||
<string name="fa_align_center"></string>
|
||||
<string name="fa_align_right"></string>
|
||||
<string name="fa_align_justify"></string>
|
||||
<string name="fa_list"></string>
|
||||
<string name="fa_outdent"></string>
|
||||
<string name="fa_indent"></string>
|
||||
<string name="fa_video_camera"></string>
|
||||
<string name="fa_picture_o"></string>
|
||||
<string name="fa_pencil"></string>
|
||||
<string name="fa_map_marker"></string>
|
||||
<string name="fa_adjust"></string>
|
||||
<string name="fa_tint"></string>
|
||||
<string name="fa_pencil_square_o"></string>
|
||||
<string name="fa_share_square_o"></string>
|
||||
<string name="fa_check_square_o"></string>
|
||||
<string name="fa_arrows"></string>
|
||||
<string name="fa_step_backward"></string>
|
||||
<string name="fa_fast_backward"></string>
|
||||
<string name="fa_backward"></string>
|
||||
<string name="fa_play"></string>
|
||||
<string name="fa_pause"></string>
|
||||
<string name="fa_stop"></string>
|
||||
<string name="fa_forward"></string>
|
||||
<string name="fa_fast_forward"></string>
|
||||
<string name="fa_step_forward"></string>
|
||||
<string name="fa_eject"></string>
|
||||
<string name="fa_chevron_left"></string>
|
||||
<string name="fa_chevron_right"></string>
|
||||
<string name="fa_plus_circle"></string>
|
||||
<string name="fa_minus_circle"></string>
|
||||
<string name="fa_times_circle"></string>
|
||||
<string name="fa_check_circle"></string>
|
||||
<string name="fa_question_circle"></string>
|
||||
<string name="fa_info_circle"></string>
|
||||
<string name="fa_crosshairs"></string>
|
||||
<string name="fa_times_circle_o"></string>
|
||||
<string name="fa_check_circle_o"></string>
|
||||
<string name="fa_ban"></string>
|
||||
<string name="fa_arrow_left"></string>
|
||||
<string name="fa_arrow_right"></string>
|
||||
<string name="fa_arrow_up"></string>
|
||||
<string name="fa_arrow_down"></string>
|
||||
<string name="fa_share"></string>
|
||||
<string name="fa_expand"></string>
|
||||
<string name="fa_compress"></string>
|
||||
<string name="fa_plus"></string>
|
||||
<string name="fa_minus"></string>
|
||||
<string name="fa_asterisk"></string>
|
||||
<string name="fa_exclamation_circle"></string>
|
||||
<string name="fa_gift"></string>
|
||||
<string name="fa_leaf"></string>
|
||||
<string name="fa_fire"></string>
|
||||
<string name="fa_eye"></string>
|
||||
<string name="fa_eye_slash"></string>
|
||||
<string name="fa_exclamation_triangle"></string>
|
||||
<string name="fa_plane"></string>
|
||||
<string name="fa_calendar"></string>
|
||||
<string name="fa_random"></string>
|
||||
<string name="fa_comment"></string>
|
||||
<string name="fa_magnet"></string>
|
||||
<string name="fa_chevron_up"></string>
|
||||
<string name="fa_chevron_down"></string>
|
||||
<string name="fa_retweet"></string>
|
||||
<string name="fa_shopping_cart"></string>
|
||||
<string name="fa_folder"></string>
|
||||
<string name="fa_folder_open"></string>
|
||||
<string name="fa_arrows_v"></string>
|
||||
<string name="fa_arrows_h"></string>
|
||||
<string name="fa_bar_chart_o"></string>
|
||||
<string name="fa_twitter_square"></string>
|
||||
<string name="fa_facebook_square"></string>
|
||||
<string name="fa_camera_retro"></string>
|
||||
<string name="fa_key"></string>
|
||||
<string name="fa_cogs"></string>
|
||||
<string name="fa_comments"></string>
|
||||
<string name="fa_thumbs_o_up"></string>
|
||||
<string name="fa_thumbs_o_down"></string>
|
||||
<string name="fa_star_half"></string>
|
||||
<string name="fa_heart_o"></string>
|
||||
<string name="fa_sign_out"></string>
|
||||
<string name="fa_linkedin_square"></string>
|
||||
<string name="fa_thumb_tack"></string>
|
||||
<string name="fa_external_link"></string>
|
||||
<string name="fa_sign_in"></string>
|
||||
<string name="fa_trophy"></string>
|
||||
<string name="fa_github_square"></string>
|
||||
<string name="fa_upload"></string>
|
||||
<string name="fa_lemon_o"></string>
|
||||
<string name="fa_phone"></string>
|
||||
<string name="fa_square_o"></string>
|
||||
<string name="fa_bookmark_o"></string>
|
||||
<string name="fa_phone_square"></string>
|
||||
<string name="fa_twitter"></string>
|
||||
<string name="fa_facebook"></string>
|
||||
<string name="fa_github"></string>
|
||||
<string name="fa_unlock"></string>
|
||||
<string name="fa_credit_card"></string>
|
||||
<string name="fa_rss"></string>
|
||||
<string name="fa_hdd_o"></string>
|
||||
<string name="fa_bullhorn"></string>
|
||||
<string name="fa_bell"></string>
|
||||
<string name="fa_certificate"></string>
|
||||
<string name="fa_hand_o_right"></string>
|
||||
<string name="fa_hand_o_left"></string>
|
||||
<string name="fa_hand_o_up"></string>
|
||||
<string name="fa_hand_o_down"></string>
|
||||
<string name="fa_arrow_circle_left"></string>
|
||||
<string name="fa_arrow_circle_right"></string>
|
||||
<string name="fa_arrow_circle_up"></string>
|
||||
<string name="fa_arrow_circle_down"></string>
|
||||
<string name="fa_globe"></string>
|
||||
<string name="fa_wrench"></string>
|
||||
<string name="fa_tasks"></string>
|
||||
<string name="fa_filter"></string>
|
||||
<string name="fa_briefcase"></string>
|
||||
<string name="fa_arrows_alt"></string>
|
||||
<string name="fa_users"></string>
|
||||
<string name="fa_link"></string>
|
||||
<string name="fa_cloud"></string>
|
||||
<string name="fa_flask"></string>
|
||||
<string name="fa_scissors"></string>
|
||||
<string name="fa_files_o"></string>
|
||||
<string name="fa_paperclip"></string>
|
||||
<string name="fa_floppy_o"></string>
|
||||
<string name="fa_square"></string>
|
||||
<string name="fa_bars"></string>
|
||||
<string name="fa_list_ul"></string>
|
||||
<string name="fa_list_ol"></string>
|
||||
<string name="fa_strikethrough"></string>
|
||||
<string name="fa_underline"></string>
|
||||
<string name="fa_table"></string>
|
||||
<string name="fa_magic"></string>
|
||||
<string name="fa_truck"></string>
|
||||
<string name="fa_pinterest"></string>
|
||||
<string name="fa_pinterest_square"></string>
|
||||
<string name="fa_google_plus_square"></string>
|
||||
<string name="fa_google_plus"></string>
|
||||
<string name="fa_money"></string>
|
||||
<string name="fa_caret_down"></string>
|
||||
<string name="fa_caret_up"></string>
|
||||
<string name="fa_caret_left"></string>
|
||||
<string name="fa_caret_right"></string>
|
||||
<string name="fa_columns"></string>
|
||||
<string name="fa_sort"></string>
|
||||
<string name="fa_sort_asc"></string>
|
||||
<string name="fa_sort_desc"></string>
|
||||
<string name="fa_envelope"></string>
|
||||
<string name="fa_linkedin"></string>
|
||||
<string name="fa_undo"></string>
|
||||
<string name="fa_gavel"></string>
|
||||
<string name="fa_tachometer"></string>
|
||||
<string name="fa_comment_o"></string>
|
||||
<string name="fa_comments_o"></string>
|
||||
<string name="fa_bolt"></string>
|
||||
<string name="fa_sitemap"></string>
|
||||
<string name="fa_umbrella"></string>
|
||||
<string name="fa_clipboard"></string>
|
||||
<string name="fa_lightbulb_o"></string>
|
||||
<string name="fa_exchange"></string>
|
||||
<string name="fa_cloud_download"></string>
|
||||
<string name="fa_cloud_upload"></string>
|
||||
<string name="fa_user_md"></string>
|
||||
<string name="fa_stethoscope"></string>
|
||||
<string name="fa_suitcase"></string>
|
||||
<string name="fa_bell_o"></string>
|
||||
<string name="fa_coffee"></string>
|
||||
<string name="fa_cutlery"></string>
|
||||
<string name="fa_file_text_o"></string>
|
||||
<string name="fa_building_o"></string>
|
||||
<string name="fa_hospital_o"></string>
|
||||
<string name="fa_ambulance"></string>
|
||||
<string name="fa_medkit"></string>
|
||||
<string name="fa_fighter_jet"></string>
|
||||
<string name="fa_beer"></string>
|
||||
<string name="fa_h_square"></string>
|
||||
<string name="fa_plus_square"></string>
|
||||
<string name="fa_angle_double_left"></string>
|
||||
<string name="fa_angle_double_right"></string>
|
||||
<string name="fa_angle_double_up"></string>
|
||||
<string name="fa_angle_double_down"></string>
|
||||
<string name="fa_angle_left"></string>
|
||||
<string name="fa_angle_right"></string>
|
||||
<string name="fa_angle_up"></string>
|
||||
<string name="fa_angle_down"></string>
|
||||
<string name="fa_desktop"></string>
|
||||
<string name="fa_laptop"></string>
|
||||
<string name="fa_tablet"></string>
|
||||
<string name="fa_mobile"></string>
|
||||
<string name="fa_circle_o"></string>
|
||||
<string name="fa_quote_left"></string>
|
||||
<string name="fa_quote_right"></string>
|
||||
<string name="fa_spinner"></string>
|
||||
<string name="fa_circle"></string>
|
||||
<string name="fa_reply"></string>
|
||||
<string name="fa_github_alt"></string>
|
||||
<string name="fa_folder_o"></string>
|
||||
<string name="fa_folder_open_o"></string>
|
||||
<string name="fa_smile_o"></string>
|
||||
<string name="fa_frown_o"></string>
|
||||
<string name="fa_meh_o"></string>
|
||||
<string name="fa_gamepad"></string>
|
||||
<string name="fa_keyboard_o"></string>
|
||||
<string name="fa_flag_o"></string>
|
||||
<string name="fa_flag_checkered"></string>
|
||||
<string name="fa_terminal"></string>
|
||||
<string name="fa_code"></string>
|
||||
<string name="fa_reply_all"></string>
|
||||
<string name="fa_mail_reply_all"></string>
|
||||
<string name="fa_star_half_o"></string>
|
||||
<string name="fa_location_arrow"></string>
|
||||
<string name="fa_crop"></string>
|
||||
<string name="fa_code_fork"></string>
|
||||
<string name="fa_chain_broken"></string>
|
||||
<string name="fa_question"></string>
|
||||
<string name="fa_info"></string>
|
||||
<string name="fa_exclamation"></string>
|
||||
<string name="fa_superscript"></string>
|
||||
<string name="fa_subscript"></string>
|
||||
<string name="fa_eraser"></string>
|
||||
<string name="fa_puzzle_piece"></string>
|
||||
<string name="fa_microphone"></string>
|
||||
<string name="fa_microphone_slash"></string>
|
||||
<string name="fa_shield"></string>
|
||||
<string name="fa_calendar_o"></string>
|
||||
<string name="fa_fire_extinguisher"></string>
|
||||
<string name="fa_rocket"></string>
|
||||
<string name="fa_maxcdn"></string>
|
||||
<string name="fa_chevron_circle_left"></string>
|
||||
<string name="fa_chevron_circle_right"></string>
|
||||
<string name="fa_chevron_circle_up"></string>
|
||||
<string name="fa_chevron_circle_down"></string>
|
||||
<string name="fa_html5"></string>
|
||||
<string name="fa_css3"></string>
|
||||
<string name="fa_anchor"></string>
|
||||
<string name="fa_unlock_alt"></string>
|
||||
<string name="fa_bullseye"></string>
|
||||
<string name="fa_ellipsis_h"></string>
|
||||
<string name="fa_ellipsis_v"></string>
|
||||
<string name="fa_rss_square"></string>
|
||||
<string name="fa_play_circle"></string>
|
||||
<string name="fa_ticket"></string>
|
||||
<string name="fa_minus_square"></string>
|
||||
<string name="fa_minus_square_o"></string>
|
||||
<string name="fa_level_up"></string>
|
||||
<string name="fa_level_down"></string>
|
||||
<string name="fa_check_square"></string>
|
||||
<string name="fa_pencil_square"></string>
|
||||
<string name="fa_external_link_square"></string>
|
||||
<string name="fa_share_square"></string>
|
||||
<string name="fa_compass"></string>
|
||||
<string name="fa_caret_square_o_down"></string>
|
||||
<string name="fa_caret_square_o_up"></string>
|
||||
<string name="fa_caret_square_o_right"></string>
|
||||
<string name="fa_eur"></string>
|
||||
<string name="fa_gbp"></string>
|
||||
<string name="fa_usd"></string>
|
||||
<string name="fa_inr"></string>
|
||||
<string name="fa_jpy"></string>
|
||||
<string name="fa_rub"></string>
|
||||
<string name="fa_krw"></string>
|
||||
<string name="fa_btc"></string>
|
||||
<string name="fa_file"></string>
|
||||
<string name="fa_file_text"></string>
|
||||
<string name="fa_sort_alpha_asc"></string>
|
||||
<string name="fa_sort_alpha_desc"></string>
|
||||
<string name="fa_sort_amount_asc"></string>
|
||||
<string name="fa_sort_amount_desc"></string>
|
||||
<string name="fa_sort_numeric_asc"></string>
|
||||
<string name="fa_sort_numeric_desc"></string>
|
||||
<string name="fa_thumbs_up"></string>
|
||||
<string name="fa_thumbs_down"></string>
|
||||
<string name="fa_youtube_square"></string>
|
||||
<string name="fa_youtube"></string>
|
||||
<string name="fa_xing"></string>
|
||||
<string name="fa_xing_square"></string>
|
||||
<string name="fa_youtube_play"></string>
|
||||
<string name="fa_dropbox"></string>
|
||||
<string name="fa_stack_overflow"></string>
|
||||
<string name="fa_instagram"></string>
|
||||
<string name="fa_flickr"></string>
|
||||
<string name="fa_adn"></string>
|
||||
<string name="fa_bitbucket"></string>
|
||||
<string name="fa_bitbucket_square"></string>
|
||||
<string name="fa_tumblr"></string>
|
||||
<string name="fa_tumblr_square"></string>
|
||||
<string name="fa_long_arrow_down"></string>
|
||||
<string name="fa_long_arrow_up"></string>
|
||||
<string name="fa_long_arrow_left"></string>
|
||||
<string name="fa_long_arrow_right"></string>
|
||||
<string name="fa_apple"></string>
|
||||
<string name="fa_windows"></string>
|
||||
<string name="fa_android"></string>
|
||||
<string name="fa_linux"></string>
|
||||
<string name="fa_dribbble"></string>
|
||||
<string name="fa_skype"></string>
|
||||
<string name="fa_foursquare"></string>
|
||||
<string name="fa_trello"></string>
|
||||
<string name="fa_female"></string>
|
||||
<string name="fa_male"></string>
|
||||
<string name="fa_gittip"></string>
|
||||
<string name="fa_sun_o"></string>
|
||||
<string name="fa_moon_o"></string>
|
||||
<string name="fa_archive"></string>
|
||||
<string name="fa_bug"></string>
|
||||
<string name="fa_vk"></string>
|
||||
<string name="fa_weibo"></string>
|
||||
<string name="fa_renren"></string>
|
||||
<string name="fa_pagelines"></string>
|
||||
<string name="fa_stack_exchange"></string>
|
||||
<string name="fa_arrow_circle_o_right"></string>
|
||||
<string name="fa_arrow_circle_o_left"></string>
|
||||
<string name="fa_caret_square_o_left"></string>
|
||||
<string name="fa_dot_circle_o"></string>
|
||||
<string name="fa_wheelchair"></string>
|
||||
<string name="fa_vimeo_square"></string>
|
||||
<string name="fa_try"></string>
|
||||
<string name="fa_plus_square_o"></string>
|
||||
<string translatable="false" name="fa_glass"></string>
|
||||
<string translatable="false" name="fa_music"></string>
|
||||
<string translatable="false" name="fa_search"></string>
|
||||
<string translatable="false" name="fa_envelope_o"></string>
|
||||
<string translatable="false" name="fa_heart"></string>
|
||||
<string translatable="false" name="fa_star"></string>
|
||||
<string translatable="false" name="fa_star_o"></string>
|
||||
<string translatable="false" name="fa_user"></string>
|
||||
<string translatable="false" name="fa_film"></string>
|
||||
<string translatable="false" name="fa_th_large"></string>
|
||||
<string translatable="false" name="fa_th"></string>
|
||||
<string translatable="false" name="fa_th_list"></string>
|
||||
<string translatable="false" name="fa_check"></string>
|
||||
<string translatable="false" name="fa_times"></string>
|
||||
<string translatable="false" name="fa_search_plus"></string>
|
||||
<string translatable="false" name="fa_search_minus"></string>
|
||||
<string translatable="false" name="fa_power_off"></string>
|
||||
<string translatable="false" name="fa_signal"></string>
|
||||
<string translatable="false" name="fa_cog"></string>
|
||||
<string translatable="false" name="fa_trash_o"></string>
|
||||
<string translatable="false" name="fa_home"></string>
|
||||
<string translatable="false" name="fa_file_o"></string>
|
||||
<string translatable="false" name="fa_clock_o"></string>
|
||||
<string translatable="false" name="fa_road"></string>
|
||||
<string translatable="false" name="fa_download"></string>
|
||||
<string translatable="false" name="fa_arrow_circle_o_down"></string>
|
||||
<string translatable="false" name="fa_arrow_circle_o_up"></string>
|
||||
<string translatable="false" name="fa_inbox"></string>
|
||||
<string translatable="false" name="fa_play_circle_o"></string>
|
||||
<string translatable="false" name="fa_repeat"></string>
|
||||
<string translatable="false" name="fa_refresh"></string>
|
||||
<string translatable="false" name="fa_list_alt"></string>
|
||||
<string translatable="false" name="fa_lock"></string>
|
||||
<string translatable="false" name="fa_flag"></string>
|
||||
<string translatable="false" name="fa_headphones"></string>
|
||||
<string translatable="false" name="fa_volume_off"></string>
|
||||
<string translatable="false" name="fa_volume_down"></string>
|
||||
<string translatable="false" name="fa_volume_up"></string>
|
||||
<string translatable="false" name="fa_qrcode"></string>
|
||||
<string translatable="false" name="fa_barcode"></string>
|
||||
<string translatable="false" name="fa_tag"></string>
|
||||
<string translatable="false" name="fa_tags"></string>
|
||||
<string translatable="false" name="fa_book"></string>
|
||||
<string translatable="false" name="fa_bookmark"></string>
|
||||
<string translatable="false" name="fa_print"></string>
|
||||
<string translatable="false" name="fa_camera"></string>
|
||||
<string translatable="false" name="fa_font"></string>
|
||||
<string translatable="false" name="fa_bold"></string>
|
||||
<string translatable="false" name="fa_italic"></string>
|
||||
<string translatable="false" name="fa_text_height"></string>
|
||||
<string translatable="false" name="fa_text_width"></string>
|
||||
<string translatable="false" name="fa_align_left"></string>
|
||||
<string translatable="false" name="fa_align_center"></string>
|
||||
<string translatable="false" name="fa_align_right"></string>
|
||||
<string translatable="false" name="fa_align_justify"></string>
|
||||
<string translatable="false" name="fa_list"></string>
|
||||
<string translatable="false" name="fa_outdent"></string>
|
||||
<string translatable="false" name="fa_indent"></string>
|
||||
<string translatable="false" name="fa_video_camera"></string>
|
||||
<string translatable="false" name="fa_picture_o"></string>
|
||||
<string translatable="false" name="fa_pencil"></string>
|
||||
<string translatable="false" name="fa_map_marker"></string>
|
||||
<string translatable="false" name="fa_adjust"></string>
|
||||
<string translatable="false" name="fa_tint"></string>
|
||||
<string translatable="false" name="fa_pencil_square_o"></string>
|
||||
<string translatable="false" name="fa_share_square_o"></string>
|
||||
<string translatable="false" name="fa_check_square_o"></string>
|
||||
<string translatable="false" name="fa_arrows"></string>
|
||||
<string translatable="false" name="fa_step_backward"></string>
|
||||
<string translatable="false" name="fa_fast_backward"></string>
|
||||
<string translatable="false" name="fa_backward"></string>
|
||||
<string translatable="false" name="fa_play"></string>
|
||||
<string translatable="false" name="fa_pause"></string>
|
||||
<string translatable="false" name="fa_stop"></string>
|
||||
<string translatable="false" name="fa_forward"></string>
|
||||
<string translatable="false" name="fa_fast_forward"></string>
|
||||
<string translatable="false" name="fa_step_forward"></string>
|
||||
<string translatable="false" name="fa_eject"></string>
|
||||
<string translatable="false" name="fa_chevron_left"></string>
|
||||
<string translatable="false" name="fa_chevron_right"></string>
|
||||
<string translatable="false" name="fa_plus_circle"></string>
|
||||
<string translatable="false" name="fa_minus_circle"></string>
|
||||
<string translatable="false" name="fa_times_circle"></string>
|
||||
<string translatable="false" name="fa_check_circle"></string>
|
||||
<string translatable="false" name="fa_question_circle"></string>
|
||||
<string translatable="false" name="fa_info_circle"></string>
|
||||
<string translatable="false" name="fa_crosshairs"></string>
|
||||
<string translatable="false" name="fa_times_circle_o"></string>
|
||||
<string translatable="false" name="fa_check_circle_o"></string>
|
||||
<string translatable="false" name="fa_ban"></string>
|
||||
<string translatable="false" name="fa_arrow_left"></string>
|
||||
<string translatable="false" name="fa_arrow_right"></string>
|
||||
<string translatable="false" name="fa_arrow_up"></string>
|
||||
<string translatable="false" name="fa_arrow_down"></string>
|
||||
<string translatable="false" name="fa_share"></string>
|
||||
<string translatable="false" name="fa_expand"></string>
|
||||
<string translatable="false" name="fa_compress"></string>
|
||||
<string translatable="false" name="fa_plus"></string>
|
||||
<string translatable="false" name="fa_minus"></string>
|
||||
<string translatable="false" name="fa_asterisk"></string>
|
||||
<string translatable="false" name="fa_exclamation_circle"></string>
|
||||
<string translatable="false" name="fa_gift"></string>
|
||||
<string translatable="false" name="fa_leaf"></string>
|
||||
<string translatable="false" name="fa_fire"></string>
|
||||
<string translatable="false" name="fa_eye"></string>
|
||||
<string translatable="false" name="fa_eye_slash"></string>
|
||||
<string translatable="false" name="fa_exclamation_triangle"></string>
|
||||
<string translatable="false" name="fa_plane"></string>
|
||||
<string translatable="false" name="fa_calendar"></string>
|
||||
<string translatable="false" name="fa_random"></string>
|
||||
<string translatable="false" name="fa_comment"></string>
|
||||
<string translatable="false" name="fa_magnet"></string>
|
||||
<string translatable="false" name="fa_chevron_up"></string>
|
||||
<string translatable="false" name="fa_chevron_down"></string>
|
||||
<string translatable="false" name="fa_retweet"></string>
|
||||
<string translatable="false" name="fa_shopping_cart"></string>
|
||||
<string translatable="false" name="fa_folder"></string>
|
||||
<string translatable="false" name="fa_folder_open"></string>
|
||||
<string translatable="false" name="fa_arrows_v"></string>
|
||||
<string translatable="false" name="fa_arrows_h"></string>
|
||||
<string translatable="false" name="fa_bar_chart_o"></string>
|
||||
<string translatable="false" name="fa_twitter_square"></string>
|
||||
<string translatable="false" name="fa_facebook_square"></string>
|
||||
<string translatable="false" name="fa_camera_retro"></string>
|
||||
<string translatable="false" name="fa_key"></string>
|
||||
<string translatable="false" name="fa_cogs"></string>
|
||||
<string translatable="false" name="fa_comments"></string>
|
||||
<string translatable="false" name="fa_thumbs_o_up"></string>
|
||||
<string translatable="false" name="fa_thumbs_o_down"></string>
|
||||
<string translatable="false" name="fa_star_half"></string>
|
||||
<string translatable="false" name="fa_heart_o"></string>
|
||||
<string translatable="false" name="fa_sign_out"></string>
|
||||
<string translatable="false" name="fa_linkedin_square"></string>
|
||||
<string translatable="false" name="fa_thumb_tack"></string>
|
||||
<string translatable="false" name="fa_external_link"></string>
|
||||
<string translatable="false" name="fa_sign_in"></string>
|
||||
<string translatable="false" name="fa_trophy"></string>
|
||||
<string translatable="false" name="fa_github_square"></string>
|
||||
<string translatable="false" name="fa_upload"></string>
|
||||
<string translatable="false" name="fa_lemon_o"></string>
|
||||
<string translatable="false" name="fa_phone"></string>
|
||||
<string translatable="false" name="fa_square_o"></string>
|
||||
<string translatable="false" name="fa_bookmark_o"></string>
|
||||
<string translatable="false" name="fa_phone_square"></string>
|
||||
<string translatable="false" name="fa_twitter"></string>
|
||||
<string translatable="false" name="fa_facebook"></string>
|
||||
<string translatable="false" name="fa_github"></string>
|
||||
<string translatable="false" name="fa_unlock"></string>
|
||||
<string translatable="false" name="fa_credit_card"></string>
|
||||
<string translatable="false" name="fa_rss"></string>
|
||||
<string translatable="false" name="fa_hdd_o"></string>
|
||||
<string translatable="false" name="fa_bullhorn"></string>
|
||||
<string translatable="false" name="fa_bell"></string>
|
||||
<string translatable="false" name="fa_certificate"></string>
|
||||
<string translatable="false" name="fa_hand_o_right"></string>
|
||||
<string translatable="false" name="fa_hand_o_left"></string>
|
||||
<string translatable="false" name="fa_hand_o_up"></string>
|
||||
<string translatable="false" name="fa_hand_o_down"></string>
|
||||
<string translatable="false" name="fa_arrow_circle_left"></string>
|
||||
<string translatable="false" name="fa_arrow_circle_right"></string>
|
||||
<string translatable="false" name="fa_arrow_circle_up"></string>
|
||||
<string translatable="false" name="fa_arrow_circle_down"></string>
|
||||
<string translatable="false" name="fa_globe"></string>
|
||||
<string translatable="false" name="fa_wrench"></string>
|
||||
<string translatable="false" name="fa_tasks"></string>
|
||||
<string translatable="false" name="fa_filter"></string>
|
||||
<string translatable="false" name="fa_briefcase"></string>
|
||||
<string translatable="false" name="fa_arrows_alt"></string>
|
||||
<string translatable="false" name="fa_users"></string>
|
||||
<string translatable="false" name="fa_link"></string>
|
||||
<string translatable="false" name="fa_cloud"></string>
|
||||
<string translatable="false" name="fa_flask"></string>
|
||||
<string translatable="false" name="fa_scissors"></string>
|
||||
<string translatable="false" name="fa_files_o"></string>
|
||||
<string translatable="false" name="fa_paperclip"></string>
|
||||
<string translatable="false" name="fa_floppy_o"></string>
|
||||
<string translatable="false" name="fa_square"></string>
|
||||
<string translatable="false" name="fa_bars"></string>
|
||||
<string translatable="false" name="fa_list_ul"></string>
|
||||
<string translatable="false" name="fa_list_ol"></string>
|
||||
<string translatable="false" name="fa_strikethrough"></string>
|
||||
<string translatable="false" name="fa_underline"></string>
|
||||
<string translatable="false" name="fa_table"></string>
|
||||
<string translatable="false" name="fa_magic"></string>
|
||||
<string translatable="false" name="fa_truck"></string>
|
||||
<string translatable="false" name="fa_pinterest"></string>
|
||||
<string translatable="false" name="fa_pinterest_square"></string>
|
||||
<string translatable="false" name="fa_google_plus_square"></string>
|
||||
<string translatable="false" name="fa_google_plus"></string>
|
||||
<string translatable="false" name="fa_money"></string>
|
||||
<string translatable="false" name="fa_caret_down"></string>
|
||||
<string translatable="false" name="fa_caret_up"></string>
|
||||
<string translatable="false" name="fa_caret_left"></string>
|
||||
<string translatable="false" name="fa_caret_right"></string>
|
||||
<string translatable="false" name="fa_columns"></string>
|
||||
<string translatable="false" name="fa_sort"></string>
|
||||
<string translatable="false" name="fa_sort_asc"></string>
|
||||
<string translatable="false" name="fa_sort_desc"></string>
|
||||
<string translatable="false" name="fa_envelope"></string>
|
||||
<string translatable="false" name="fa_linkedin"></string>
|
||||
<string translatable="false" name="fa_undo"></string>
|
||||
<string translatable="false" name="fa_gavel"></string>
|
||||
<string translatable="false" name="fa_tachometer"></string>
|
||||
<string translatable="false" name="fa_comment_o"></string>
|
||||
<string translatable="false" name="fa_comments_o"></string>
|
||||
<string translatable="false" name="fa_bolt"></string>
|
||||
<string translatable="false" name="fa_sitemap"></string>
|
||||
<string translatable="false" name="fa_umbrella"></string>
|
||||
<string translatable="false" name="fa_clipboard"></string>
|
||||
<string translatable="false" name="fa_lightbulb_o"></string>
|
||||
<string translatable="false" name="fa_exchange"></string>
|
||||
<string translatable="false" name="fa_cloud_download"></string>
|
||||
<string translatable="false" name="fa_cloud_upload"></string>
|
||||
<string translatable="false" name="fa_user_md"></string>
|
||||
<string translatable="false" name="fa_stethoscope"></string>
|
||||
<string translatable="false" name="fa_suitcase"></string>
|
||||
<string translatable="false" name="fa_bell_o"></string>
|
||||
<string translatable="false" name="fa_coffee"></string>
|
||||
<string translatable="false" name="fa_cutlery"></string>
|
||||
<string translatable="false" name="fa_file_text_o"></string>
|
||||
<string translatable="false" name="fa_building_o"></string>
|
||||
<string translatable="false" name="fa_hospital_o"></string>
|
||||
<string translatable="false" name="fa_ambulance"></string>
|
||||
<string translatable="false" name="fa_medkit"></string>
|
||||
<string translatable="false" name="fa_fighter_jet"></string>
|
||||
<string translatable="false" name="fa_beer"></string>
|
||||
<string translatable="false" name="fa_h_square"></string>
|
||||
<string translatable="false" name="fa_plus_square"></string>
|
||||
<string translatable="false" name="fa_angle_double_left"></string>
|
||||
<string translatable="false" name="fa_angle_double_right"></string>
|
||||
<string translatable="false" name="fa_angle_double_up"></string>
|
||||
<string translatable="false" name="fa_angle_double_down"></string>
|
||||
<string translatable="false" name="fa_angle_left"></string>
|
||||
<string translatable="false" name="fa_angle_right"></string>
|
||||
<string translatable="false" name="fa_angle_up"></string>
|
||||
<string translatable="false" name="fa_angle_down"></string>
|
||||
<string translatable="false" name="fa_desktop"></string>
|
||||
<string translatable="false" name="fa_laptop"></string>
|
||||
<string translatable="false" name="fa_tablet"></string>
|
||||
<string translatable="false" name="fa_mobile"></string>
|
||||
<string translatable="false" name="fa_circle_o"></string>
|
||||
<string translatable="false" name="fa_quote_left"></string>
|
||||
<string translatable="false" name="fa_quote_right"></string>
|
||||
<string translatable="false" name="fa_spinner"></string>
|
||||
<string translatable="false" name="fa_circle"></string>
|
||||
<string translatable="false" name="fa_reply"></string>
|
||||
<string translatable="false" name="fa_github_alt"></string>
|
||||
<string translatable="false" name="fa_folder_o"></string>
|
||||
<string translatable="false" name="fa_folder_open_o"></string>
|
||||
<string translatable="false" name="fa_smile_o"></string>
|
||||
<string translatable="false" name="fa_frown_o"></string>
|
||||
<string translatable="false" name="fa_meh_o"></string>
|
||||
<string translatable="false" name="fa_gamepad"></string>
|
||||
<string translatable="false" name="fa_keyboard_o"></string>
|
||||
<string translatable="false" name="fa_flag_o"></string>
|
||||
<string translatable="false" name="fa_flag_checkered"></string>
|
||||
<string translatable="false" name="fa_terminal"></string>
|
||||
<string translatable="false" name="fa_code"></string>
|
||||
<string translatable="false" name="fa_reply_all"></string>
|
||||
<string translatable="false" name="fa_mail_reply_all"></string>
|
||||
<string translatable="false" name="fa_star_half_o"></string>
|
||||
<string translatable="false" name="fa_location_arrow"></string>
|
||||
<string translatable="false" name="fa_crop"></string>
|
||||
<string translatable="false" name="fa_code_fork"></string>
|
||||
<string translatable="false" name="fa_chain_broken"></string>
|
||||
<string translatable="false" name="fa_question"></string>
|
||||
<string translatable="false" name="fa_info"></string>
|
||||
<string translatable="false" name="fa_exclamation"></string>
|
||||
<string translatable="false" name="fa_superscript"></string>
|
||||
<string translatable="false" name="fa_subscript"></string>
|
||||
<string translatable="false" name="fa_eraser"></string>
|
||||
<string translatable="false" name="fa_puzzle_piece"></string>
|
||||
<string translatable="false" name="fa_microphone"></string>
|
||||
<string translatable="false" name="fa_microphone_slash"></string>
|
||||
<string translatable="false" name="fa_shield"></string>
|
||||
<string translatable="false" name="fa_calendar_o"></string>
|
||||
<string translatable="false" name="fa_fire_extinguisher"></string>
|
||||
<string translatable="false" name="fa_rocket"></string>
|
||||
<string translatable="false" name="fa_maxcdn"></string>
|
||||
<string translatable="false" name="fa_chevron_circle_left"></string>
|
||||
<string translatable="false" name="fa_chevron_circle_right"></string>
|
||||
<string translatable="false" name="fa_chevron_circle_up"></string>
|
||||
<string translatable="false" name="fa_chevron_circle_down"></string>
|
||||
<string translatable="false" name="fa_html5"></string>
|
||||
<string translatable="false" name="fa_css3"></string>
|
||||
<string translatable="false" name="fa_anchor"></string>
|
||||
<string translatable="false" name="fa_unlock_alt"></string>
|
||||
<string translatable="false" name="fa_bullseye"></string>
|
||||
<string translatable="false" name="fa_ellipsis_h"></string>
|
||||
<string translatable="false" name="fa_ellipsis_v"></string>
|
||||
<string translatable="false" name="fa_rss_square"></string>
|
||||
<string translatable="false" name="fa_play_circle"></string>
|
||||
<string translatable="false" name="fa_ticket"></string>
|
||||
<string translatable="false" name="fa_minus_square"></string>
|
||||
<string translatable="false" name="fa_minus_square_o"></string>
|
||||
<string translatable="false" name="fa_level_up"></string>
|
||||
<string translatable="false" name="fa_level_down"></string>
|
||||
<string translatable="false" name="fa_check_square"></string>
|
||||
<string translatable="false" name="fa_pencil_square"></string>
|
||||
<string translatable="false" name="fa_external_link_square"></string>
|
||||
<string translatable="false" name="fa_share_square"></string>
|
||||
<string translatable="false" name="fa_compass"></string>
|
||||
<string translatable="false" name="fa_caret_square_o_down"></string>
|
||||
<string translatable="false" name="fa_caret_square_o_up"></string>
|
||||
<string translatable="false" name="fa_caret_square_o_right"></string>
|
||||
<string translatable="false" name="fa_eur"></string>
|
||||
<string translatable="false" name="fa_gbp"></string>
|
||||
<string translatable="false" name="fa_usd"></string>
|
||||
<string translatable="false" name="fa_inr"></string>
|
||||
<string translatable="false" name="fa_jpy"></string>
|
||||
<string translatable="false" name="fa_rub"></string>
|
||||
<string translatable="false" name="fa_krw"></string>
|
||||
<string translatable="false" name="fa_btc"></string>
|
||||
<string translatable="false" name="fa_file"></string>
|
||||
<string translatable="false" name="fa_file_text"></string>
|
||||
<string translatable="false" name="fa_sort_alpha_asc"></string>
|
||||
<string translatable="false" name="fa_sort_alpha_desc"></string>
|
||||
<string translatable="false" name="fa_sort_amount_asc"></string>
|
||||
<string translatable="false" name="fa_sort_amount_desc"></string>
|
||||
<string translatable="false" name="fa_sort_numeric_asc"></string>
|
||||
<string translatable="false" name="fa_sort_numeric_desc"></string>
|
||||
<string translatable="false" name="fa_thumbs_up"></string>
|
||||
<string translatable="false" name="fa_thumbs_down"></string>
|
||||
<string translatable="false" name="fa_youtube_square"></string>
|
||||
<string translatable="false" name="fa_youtube"></string>
|
||||
<string translatable="false" name="fa_xing"></string>
|
||||
<string translatable="false" name="fa_xing_square"></string>
|
||||
<string translatable="false" name="fa_youtube_play"></string>
|
||||
<string translatable="false" name="fa_dropbox"></string>
|
||||
<string translatable="false" name="fa_stack_overflow"></string>
|
||||
<string translatable="false" name="fa_instagram"></string>
|
||||
<string translatable="false" name="fa_flickr"></string>
|
||||
<string translatable="false" name="fa_adn"></string>
|
||||
<string translatable="false" name="fa_bitbucket"></string>
|
||||
<string translatable="false" name="fa_bitbucket_square"></string>
|
||||
<string translatable="false" name="fa_tumblr"></string>
|
||||
<string translatable="false" name="fa_tumblr_square"></string>
|
||||
<string translatable="false" name="fa_long_arrow_down"></string>
|
||||
<string translatable="false" name="fa_long_arrow_up"></string>
|
||||
<string translatable="false" name="fa_long_arrow_left"></string>
|
||||
<string translatable="false" name="fa_long_arrow_right"></string>
|
||||
<string translatable="false" name="fa_apple"></string>
|
||||
<string translatable="false" name="fa_windows"></string>
|
||||
<string translatable="false" name="fa_android"></string>
|
||||
<string translatable="false" name="fa_linux"></string>
|
||||
<string translatable="false" name="fa_dribbble"></string>
|
||||
<string translatable="false" name="fa_skype"></string>
|
||||
<string translatable="false" name="fa_foursquare"></string>
|
||||
<string translatable="false" name="fa_trello"></string>
|
||||
<string translatable="false" name="fa_female"></string>
|
||||
<string translatable="false" name="fa_male"></string>
|
||||
<string translatable="false" name="fa_gittip"></string>
|
||||
<string translatable="false" name="fa_sun_o"></string>
|
||||
<string translatable="false" name="fa_moon_o"></string>
|
||||
<string translatable="false" name="fa_archive"></string>
|
||||
<string translatable="false" name="fa_bug"></string>
|
||||
<string translatable="false" name="fa_vk"></string>
|
||||
<string translatable="false" name="fa_weibo"></string>
|
||||
<string translatable="false" name="fa_renren"></string>
|
||||
<string translatable="false" name="fa_pagelines"></string>
|
||||
<string translatable="false" name="fa_stack_exchange"></string>
|
||||
<string translatable="false" name="fa_arrow_circle_o_right"></string>
|
||||
<string translatable="false" name="fa_arrow_circle_o_left"></string>
|
||||
<string translatable="false" name="fa_caret_square_o_left"></string>
|
||||
<string translatable="false" name="fa_dot_circle_o"></string>
|
||||
<string translatable="false" name="fa_wheelchair"></string>
|
||||
<string translatable="false" name="fa_vimeo_square"></string>
|
||||
<string translatable="false" name="fa_try"></string>
|
||||
<string translatable="false" name="fa_plus_square_o"></string>
|
||||
</resources>
|
||||
|
||||
26
app/src/main/res/values/keys.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2016 Alinson Santos Xavier
|
||||
~
|
||||
~ This program 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.
|
||||
~
|
||||
~ This program 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/>.
|
||||
~
|
||||
~
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="habit_key" translatable="false" />
|
||||
<string name="offset_key" translatable="false" />
|
||||
<string name="toggle_key" translatable="false" />
|
||||
<item name="timestamp_key" type="id"/>
|
||||
</resources>
|
||||
@@ -1,66 +1,119 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
|
||||
<string name="app_name">Habits</string>
|
||||
<string name="app_name">Loop Habit Tracker</string>
|
||||
<string name="main_activity_title">Habits</string>
|
||||
<string name="action_settings">Settings</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="archive">Archive</string>
|
||||
<string name="unarchive">Unarchive</string>
|
||||
<string name="add_habit">Add habit</string>
|
||||
<string name="color_picker_default_title">Select a Color</string>
|
||||
<string name="color_swatch_description">Color <xliff:g id="color_index" example="14">%1$d</xliff:g></string>
|
||||
<string name="color_swatch_description_selected">Color <xliff:g id="color_index" example="14">%1$d</xliff:g> selected</string>
|
||||
<string name="color_picker_default_title">Change color</string>
|
||||
|
||||
<string name="toast_habit_created">Habit created.</string>
|
||||
<string name="toast_habit_deleted">Habit deleted.</string>
|
||||
<string name="toast_habit_deleted">Habits deleted.</string>
|
||||
<string name="toast_habit_restored">Habits restored.</string>
|
||||
<string name="toast_nothing_to_undo">Nothing to undo.</string>
|
||||
<string name="toast_nothing_to_redo">Nothing to redo.</string>
|
||||
<string name="toast_habit_changed">Habit changed.</string>
|
||||
<string name="toast_habit_changed_back">Habit changed back.</string>
|
||||
<string name="toast_repetition_toggled">Repetition toggled.</string>
|
||||
<string name="toast_habit_archived">Habit archived.</string>
|
||||
<string name="toast_habit_unarchived">Habit unarchived.</string>
|
||||
<string name="toast_habit_archived">Habits archived.</string>
|
||||
<string name="toast_habit_unarchived">Habits unarchived.</string>
|
||||
|
||||
<string name="color_swatch_description" translatable="false">Color <xliff:g id="color_index" example="14">%1$d</xliff:g></string>
|
||||
<string name="color_swatch_description_selected" translatable="false">Color <xliff:g id="color_index" example="14">%1$d</xliff:g> selected</string>
|
||||
|
||||
<!-- Date and time picker -->
|
||||
<string name="done_label">Done</string>
|
||||
<string name="clear_label">Clear</string>
|
||||
<string name="hour_picker_description">Hours circular slider</string>
|
||||
<string name="minute_picker_description">Minutes circular slider</string>
|
||||
<string name="hour_picker_description" translatable="false">Hours circular slider</string>
|
||||
<string name="minute_picker_description" translatable="false">Minutes circular slider</string>
|
||||
<string name="select_hours">Select hours</string>
|
||||
<string name="select_minutes">Select minutes</string>
|
||||
<string name="day_picker_description">Month grid of days</string>
|
||||
<string name="year_picker_description">Year list</string>
|
||||
<string name="day_picker_description" translatable="false">Month grid of days</string>
|
||||
<string name="year_picker_description" translatable="false">Year list</string>
|
||||
<string name="select_day">Select month and day</string>
|
||||
<string name="select_year">Select year</string>
|
||||
<string name="item_is_selected"><xliff:g id="item" example="2013">%1$s</xliff:g> selected</string>
|
||||
<string name="deleted_key"><xliff:g id="key" example="4">%1$s</xliff:g> deleted</string>
|
||||
<string name="time_placeholder">--</string>
|
||||
<string name="time_separator">:</string>
|
||||
<string name="radial_numbers_typeface">sans-serif</string>
|
||||
<string name="sans_serif">sans-serif</string>
|
||||
<string name="day_of_week_label_typeface">sans-serif</string>
|
||||
<string name="habit_key"></string>
|
||||
<string name="offset_key"></string>
|
||||
<string name="toggle_key"></string>
|
||||
<string name="item_is_selected" translatable="false"><xliff:g id="item" example="2013">%1$s</xliff:g> selected</string>
|
||||
<string name="deleted_key" translatable="false"><xliff:g id="key" example="4">%1$s</xliff:g> deleted</string>
|
||||
<string name="time_placeholder" translatable="false">--</string>
|
||||
<string name="time_separator" translatable="false">:</string>
|
||||
<string name="radial_numbers_typeface" translatable="false">sans-serif</string>
|
||||
<string name="sans_serif" translatable="false">sans-serif</string>
|
||||
<string name="day_of_week_label_typeface" translatable="false">sans-serif</string>
|
||||
|
||||
<item name="KEY_TIMESTAMP" type="id"/>
|
||||
|
||||
<string name="title_activity_show_habit"></string>
|
||||
<string name="hello_world">Hello world!</string>
|
||||
<string name="title_activity_show_habit" translatable="false"/>
|
||||
<string name="overview">Overview</string>
|
||||
<string name="habit_strength">Habit strength</string>
|
||||
<string name="history">History</string>
|
||||
<string name="clear">Clear</string>
|
||||
<string name="description">Description</string>
|
||||
<string name="description_hint">Question (Did you ... today?)</string>
|
||||
<string name="description_hint">Question (Did you … today?)</string>
|
||||
<string name="repeat">Repeat</string>
|
||||
<string name="times_every">times every</string>
|
||||
<string name="times_every">times in</string>
|
||||
<string name="days">days</string>
|
||||
<string name="reminder">Reminder</string>
|
||||
<string name="discard">Discard</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="streaks">Streaks</string>
|
||||
<string name="no_habits_found">You have no active habits</string>
|
||||
<string name="long_press_to_toggle">Long press to check or uncheck</string>
|
||||
<string name="long_press_to_toggle">Press-and-hold to check or uncheck</string>
|
||||
<string name="reminder_off">Off</string>
|
||||
<string name="validation_name_should_not_be_blank">Name cannot be blank.</string>
|
||||
<string name="validation_number_should_be_positive">Number must be positive.</string>
|
||||
<string name="validation_at_most_one_rep_per_day">You can have at most one repetition per day</string>
|
||||
<string name="create_habit">Create habit</string>
|
||||
<string name="edit_habit">Edit habit</string>
|
||||
<string name="check">Check</string>
|
||||
<string name="snooze">Later</string>
|
||||
|
||||
<!-- App introduction -->
|
||||
<string name="intro_title_1">Welcome</string>
|
||||
<string name="intro_description_1">Loop Habit Tracker helps you create and maintain good habits.</string>
|
||||
|
||||
<string name="intro_title_2">Create some new habits</string>
|
||||
<string name="intro_description_2">Every day, after performing your habit, put a checkmark on the app.</string>
|
||||
|
||||
<string name="intro_title_3">Keep doing it</string>
|
||||
<string name="intro_description_3">Habits performed consistently for a long time will earn a full star.</string>
|
||||
|
||||
<string name="intro_title_4">Track your progress</string>
|
||||
<string name="intro_description_4">Detailed graphs show you how your habits improved over time.</string>
|
||||
|
||||
<string name="interval_15_minutes">15 minutes</string>
|
||||
<string name="interval_30_minutes">30 minutes</string>
|
||||
<string name="interval_1_hour">1 hour</string>
|
||||
<string name="interval_2_hour">2 hours</string>
|
||||
<string name="interval_4_hour">4 hours</string>
|
||||
<string name="interval_8_hour">8 hours</string>
|
||||
<string name="pref_toggle_title">Toggle repetitions with short press</string>
|
||||
<string name="pref_toggle_description">More convenient, but might cause accidental toggles.</string>
|
||||
<string name="pref_snooze_interval_title">Snooze interval on reminders</string>
|
||||
<string name="pref_rate_this_app">Rate this app in Google Play</string>
|
||||
<string name="pref_send_feedback">Send feedback to developer</string>
|
||||
<string name="pref_view_source_code">View source code at GitHub</string>
|
||||
<string name="pref_view_app_introduction">View app introduction</string>
|
||||
<string name="links">Links</string>
|
||||
<string name="behavior">Behavior</string>
|
||||
<string name="name">Name</string>
|
||||
<string name="show_archived">Show archived</string>
|
||||
<string name="settings">Settings</string>
|
||||
<string name="snooze_interval">Snooze interval</string>
|
||||
|
||||
<string name="hint_title">Did you know?</string>
|
||||
<string name="hint_drag">To rearrange the entries, press-and-hold on the name of the habit, then drag it to the correct place.</string>
|
||||
<string name="hint_landscape">You can see more days by putting your phone in landscape mode.</string>
|
||||
<string name="delete_habits">Delete Habits</string>
|
||||
<string name="delete_habits_message">The habits will be permanently deleted. This action cannot be undone.</string>
|
||||
<string name="weekends">Weekends</string>
|
||||
<string name="any_weekday">Any weekday</string>
|
||||
<string name="any_day">Any day</string>
|
||||
|
||||
<string-array name="hints">
|
||||
<item>@string/hint_drag</item>
|
||||
<item>@string/hint_landscape</item>
|
||||
</string-array>
|
||||
|
||||
</resources>
|
||||
@@ -40,7 +40,7 @@
|
||||
</style>
|
||||
|
||||
<style name="dialogFormTimePicker" parent="android:Widget.DeviceDefault.Light.Spinner">
|
||||
<item name="android:layout_width">100dp</item>
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:paddingLeft">12dp</item>
|
||||
</style>
|
||||
|
||||