diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 33ab1270..2a926041 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -21,8 +21,8 @@ + android:versionCode="28" + android:versionName="1.7.1"> diff --git a/app/src/main/java/org/isoron/uhabits/HabitsApplication.java b/app/src/main/java/org/isoron/uhabits/HabitsApplication.java index bad1c4ee..8e2e1e8a 100644 --- a/app/src/main/java/org/isoron/uhabits/HabitsApplication.java +++ b/app/src/main/java/org/isoron/uhabits/HabitsApplication.java @@ -21,10 +21,10 @@ package org.isoron.uhabits; import android.app.*; import android.content.*; -import android.support.annotation.*; import com.activeandroid.*; +import org.isoron.uhabits.models.sqlite.*; import org.isoron.uhabits.notifications.*; import org.isoron.uhabits.preferences.*; import org.isoron.uhabits.tasks.*; @@ -88,7 +88,16 @@ public class HabitsApplication extends Application if (db.exists()) db.delete(); } - DatabaseUtils.initializeActiveAndroid(context); + try + { + DatabaseUtils.initializeActiveAndroid(context); + } + catch (InvalidDatabaseVersionException e) + { + File db = DatabaseUtils.getDatabaseFile(context); + db.renameTo(new File(db.getAbsolutePath() + ".invalid")); + DatabaseUtils.initializeActiveAndroid(context); + } widgetUpdater = component.getWidgetUpdater(); widgetUpdater.startListening(); diff --git a/app/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java b/app/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java index 9f61d88f..fe33993d 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java +++ b/app/src/main/java/org/isoron/uhabits/activities/common/views/BundleSavedState.java @@ -20,9 +20,8 @@ package org.isoron.uhabits.activities.common.views; import android.os.*; -import android.view.*; -public class BundleSavedState extends View.BaseSavedState +public class BundleSavedState extends android.support.v4.view.AbsSavedState { public static final Parcelable.Creator CREATOR = new Parcelable.Creator() @@ -51,7 +50,7 @@ public class BundleSavedState extends View.BaseSavedState public BundleSavedState(Parcel source) { super(source); - this.bundle = source.readBundle(); + this.bundle = source.readBundle(getClass().getClassLoader()); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java index e4863cd5..4eb1f431 100644 --- a/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java +++ b/app/src/main/java/org/isoron/uhabits/activities/habits/list/views/HeaderView.java @@ -50,8 +50,6 @@ public class HeaderView extends ScrollableChart private RectF rect; - private int maxDataOffset; - public HeaderView(Context context, AttributeSet attrs) { super(context, attrs); @@ -76,7 +74,6 @@ public class HeaderView extends ScrollableChart Resources res = context.getResources(); setScrollerBucketSize((int) res.getDimension(R.dimen.checkmarkWidth)); - setDirection(shouldReverseCheckmarks() ? 1 : -1); StyledResources sr = new StyledResources(context); paint = new TextPaint(); @@ -99,7 +96,7 @@ public class HeaderView extends ScrollableChart @Override public void onCheckmarkOrderChanged() { - setDirection(shouldReverseCheckmarks() ? 1 : -1); + updateDirection(); postInvalidate(); } @@ -112,11 +109,20 @@ public class HeaderView extends ScrollableChart @Override protected void onAttachedToWindow() { + updateDirection(); super.onAttachedToWindow(); if (prefs != null) prefs.addListener(this); if (midnightTimer != null) midnightTimer.addListener(this); } + private void updateDirection() + { + int direction = -1; + if (shouldReverseCheckmarks()) direction *= -1; + if (InterfaceUtils.isLayoutRtl(this)) direction *= -1; + setDirection(direction); + } + @Override protected void onDetachedFromWindow() { @@ -145,6 +151,7 @@ public class HeaderView extends ScrollableChart float width = res.getDimension(R.dimen.checkmarkWidth); float height = res.getDimension(R.dimen.checkmarkHeight); boolean reverse = shouldReverseCheckmarks(); + boolean isRtl = InterfaceUtils.isLayoutRtl(this); day.add(GregorianCalendar.DAY_OF_MONTH, -getDataOffset()); float em = paint.measureText("m"); @@ -153,9 +160,13 @@ public class HeaderView extends ScrollableChart { rect.set(0, 0, width, height); rect.offset(canvas.getWidth(), 0); + if(reverse) rect.offset(- (i + 1) * width, 0); else rect.offset((i - buttonCount) * width, 0); + if (isRtl) rect.set(canvas.getWidth() - rect.right, rect.top, + canvas.getWidth() - rect.left, rect.bottom); + String text = DateUtils.formatHeaderDate(day).toUpperCase(); String[] lines = text.split("\n"); diff --git a/app/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java b/app/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java index a3ebcf88..bfcf5886 100644 --- a/app/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java +++ b/app/src/main/java/org/isoron/uhabits/io/LoopDBImporter.java @@ -19,20 +19,20 @@ package org.isoron.uhabits.io; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.support.annotation.NonNull; +import android.content.*; +import android.database.*; +import android.database.sqlite.*; +import android.support.annotation.*; +import android.util.*; -import com.activeandroid.ActiveAndroid; +import com.activeandroid.*; -import org.isoron.uhabits.AppContext; +import org.isoron.uhabits.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.utils.DatabaseUtils; -import org.isoron.uhabits.utils.FileUtils; +import org.isoron.uhabits.utils.*; -import java.io.File; -import java.io.IOException; +import java.io.*; import javax.inject.*; @@ -45,7 +45,8 @@ public class LoopDBImporter extends AbstractImporter private Context context; @Inject - public LoopDBImporter(@NonNull @AppContext Context context, @NonNull HabitList habits) + public LoopDBImporter(@NonNull @AppContext Context context, + @NonNull HabitList habits) { super(habits); this.context = context; @@ -59,15 +60,29 @@ public class LoopDBImporter extends AbstractImporter SQLiteDatabase db = SQLiteDatabase.openDatabase(file.getPath(), null, SQLiteDatabase.OPEN_READONLY); + boolean canHandle = true; + Cursor c = db.rawQuery( "select count(*) from SQLITE_MASTER where name=? or name=?", - new String[]{"Checkmarks", "Repetitions"}); + new String[]{ "Checkmarks", "Repetitions" }); - boolean result = (c.moveToFirst() && c.getInt(0) == 2); + if (!c.moveToFirst() || c.getInt(0) != 2) + { + Log.w("LoopDBImporter", "Cannot handle file: tables not found"); + canHandle = false; + } + + if (db.getVersion() > BuildConfig.databaseVersion) + { + Log.w("LoopDBImporter", String.format( + "Cannot handle file: incompatible version: %d > %d", + db.getVersion(), BuildConfig.databaseVersion)); + canHandle = false; + } c.close(); db.close(); - return result; + return canHandle; } @Override diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/InvalidDatabaseVersionException.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/InvalidDatabaseVersionException.java new file mode 100644 index 00000000..6fb417cc --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/InvalidDatabaseVersionException.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.models.sqlite; + +public class InvalidDatabaseVersionException extends RuntimeException +{ +} diff --git a/app/src/main/java/org/isoron/uhabits/preferences/WidgetPreferences.java b/app/src/main/java/org/isoron/uhabits/preferences/WidgetPreferences.java index f7830e2b..56566f63 100644 --- a/app/src/main/java/org/isoron/uhabits/preferences/WidgetPreferences.java +++ b/app/src/main/java/org/isoron/uhabits/preferences/WidgetPreferences.java @@ -23,6 +23,7 @@ import android.content.*; import android.preference.*; import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; import javax.inject.*; @@ -48,7 +49,7 @@ public class WidgetPreferences public long getHabitIdFromWidgetId(int widgetId) { Long habitId = prefs.getLong(getHabitIdKey(widgetId), -1); - if (habitId < 0) throw new RuntimeException("widget not found"); + if (habitId < 0) throw new HabitNotFoundException(); return habitId; } diff --git a/app/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java b/app/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java index 5e6d9e97..4db87a8d 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java +++ b/app/src/main/java/org/isoron/uhabits/utils/DatabaseUtils.java @@ -25,6 +25,7 @@ import android.support.annotation.*; import com.activeandroid.*; import org.isoron.uhabits.*; +import org.isoron.uhabits.models.sqlite.*; import org.isoron.uhabits.models.sqlite.records.*; import java.io.*; @@ -76,7 +77,16 @@ public abstract class DatabaseUtils RepetitionRecord.class, ScoreRecord.class, StreakRecord.class) .create(); - ActiveAndroid.initialize(dbConfig); + try + { + ActiveAndroid.initialize(dbConfig); + } + catch (RuntimeException e) + { + if(e.getMessage().contains("downgrade")) + throw new InvalidDatabaseVersionException(); + else throw e; + } } @SuppressWarnings("ResultOfMethodCallIgnored") diff --git a/app/src/main/java/org/isoron/uhabits/utils/InterfaceUtils.java b/app/src/main/java/org/isoron/uhabits/utils/InterfaceUtils.java index 946efc0a..645c6192 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/InterfaceUtils.java +++ b/app/src/main/java/org/isoron/uhabits/utils/InterfaceUtils.java @@ -23,6 +23,7 @@ import android.content.*; import android.content.res.*; import android.graphics.*; import android.support.annotation.*; +import android.support.v4.view.*; import android.util.*; import android.view.*; import android.widget.*; @@ -67,5 +68,10 @@ public abstract class InterfaceUtils if (child instanceof TextView) ((TextView) child).setOnEditorActionListener(listener); } + + public static boolean isLayoutRtl(View view) + { + return ViewCompat.getLayoutDirection(view) == + ViewCompat.LAYOUT_DIRECTION_RTL; } } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java index 6c6008d3..e0a18885 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java @@ -25,6 +25,8 @@ import android.os.*; import android.support.annotation.*; import android.widget.*; +import com.activeandroid.util.*; + import org.isoron.uhabits.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.preferences.*; @@ -76,8 +78,15 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider for (int id : ids) { - BaseWidget widget = getWidgetFromId(context, id); - widget.delete(); + try + { + BaseWidget widget = getWidgetFromId(context, id); + widget.delete(); + } + catch (HabitNotFoundException e) + { + Log.e("BaseWidgetProvider", e); + } } } diff --git a/app/src/main/res/layout/about.xml b/app/src/main/res/layout/about.xml index 5fe1b1a2..2869631d 100644 --- a/app/src/main/res/layout/about.xml +++ b/app/src/main/res/layout/about.xml @@ -317,6 +317,10 @@ + + diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml index 45158aac..c37f603f 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -91,6 +91,7 @@ 2 घंटा 4 घंटा 8 घंटा + 24 घंटे टॉगल पुनरावृत्ति हल्का दबाने से \" अधिक सुविधाजनक है, लेकिन आकस्मिक टॉगल हो सकता है ।\" @@ -179,7 +180,7 @@ repetitions की संख्या\" इस फ़ाइल में वापस आयात नहीं किया जा सकता है।\" ऐसी फाइल्स उत्पन्न करता है जिसमे आपका सारा डेटा रहता है इस फ़ाइल को वापस आयात किया जा सकता है। बग रिपोर्ट जनरेट करने मे असफल - बग रिपोर्ट जनरेट करने मे सफल + बग रिपोर्ट जनरेट करें \" समस्या निवारण\" \" @@ -198,15 +199,26 @@ repetitions की संख्या\" तिमाही साल - समय शुरू + समय में \" हर %d दिन\" \" हर %d हफ्ते\" \" -हर %d साल\" +हर %d महीने\" स्कोर अनुस्मारक ध्वनि \" कोई आवाज नहीं\" + फिल्टर + डेटाबेस को रिपेयर करें + डेटाबेस रिपेयर सफल + आदत + सॉर्ट करें + मैन्यूअली + नाम द्वारा + रंग द्वारा + स्कोर से + डाउनलोड + एक्सपोर्ट करे diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml index cc808293..4602cbfd 100644 --- a/app/src/main/res/values-id/strings.xml +++ b/app/src/main/res/values-id/strings.xml @@ -19,6 +19,7 @@ ~ with this program. If not, see . --> + Loop Habit Tracker Kebiasaan Pengaturan Ubah @@ -73,6 +74,7 @@ 2 jam 4 jam 8 jam + 24 jam Tandai dengan cepat. Lebih nyaman namun memungkinkan kesalahan. Durasi tunda sejenak pada pengingat