Compare commits

...

34 Commits

Author SHA1 Message Date
957a5b7c17 Fix CSV export in some locales; bump version to 1.7.11 (38) 2019-08-10 11:42:25 -05:00
64cc9f78a8 Increase targetSdk to 28 2019-06-15 09:46:06 -05:00
e50c411d1e Fix crash preventing some Xiaomi devices from showing notifications 2019-06-15 08:57:11 -05:00
ce27773138 Merge branch 'hotfix/1.7.9' 2018-04-21 10:31:54 -05:00
0864f83307 Update CHANGELOG 2018-04-21 10:17:38 -05:00
624cc67d9b Update translations 2018-04-21 10:17:31 -05:00
462bac8167 Add support for adaptive icons
Closes #395
2018-04-21 09:39:30 -05:00
5865eb41f7 Update notification for Android Oreo
- Add link to notification channel settings
- Remove snooze button

Closes #400
2018-04-21 08:50:53 -05:00
2bfd4a942d Remove unused PebbleReceiverTest 2018-04-21 08:00:00 -05:00
b4a33cba39 Add ActiveAndroid source code to our tree and remove content providers
ActiveAndroid is not actively maintained anymore and contains code
related to that Content Providers that makes the application crash on
Android Oreo.
2018-04-14 10:22:31 -05:00
f02c86e61b Bump targetSdkVersion to 27 2018-04-14 10:19:34 -05:00
5021f50e18 Bump version to 1.7.9 (36) 2018-04-14 08:11:22 -05:00
bf3964a231 Merge branch 'hotfix/1.7.7' 2017-09-30 13:27:45 -05:00
657cde75d8 Disable signing config 2017-09-30 13:18:26 -05:00
c56b86d32c Bump version again
The previous number was already in use by an alpha version
2017-09-30 13:14:42 -05:00
16f20d50a0 Fix bug that caused reminders to keep repeating every few seconds
Fixes #351
2017-09-30 13:11:40 -05:00
7bb88dcb97 Bump version and update CHANGELOG 2017-09-30 12:59:02 -05:00
7bb62c197f Include Play Store plugin 2017-07-26 10:07:25 -04:00
540a618ba8 Merge branch 'hotfix/1.7.6' 2017-07-18 22:45:50 -04:00
bf24cc608c Update changelog 2017-07-18 22:45:36 -04:00
a73459784e Fix NPE in HabitCardView.triggerRipple 2017-07-18 22:16:11 -04:00
de3b97dfdf Fix IndexOutOfBoundsException in HabitCardListAdapter 2017-07-18 22:13:10 -04:00
91996924d9 Never store reference to SQLiteDatabase
SQLiteDatabase closes automatically at random times. Storing a reference
to an open SQLiteDatabase eventually leads to a crash.
See: https://stackoverflow.com/questions/1483629/
2017-07-18 22:07:21 -04:00
9fe446b424 Fix crash when tapping HistoryChart 2017-07-18 21:55:15 -04:00
3857eaf5e9 Update changelog 2017-07-18 21:47:25 -04:00
e29fb58922 Fix failing tests 2017-07-18 21:47:19 -04:00
404fc869b0 Repair inconsistent data instead of throwing exception 2017-07-18 21:28:48 -04:00
001dd5a7c1 Update translations 2017-07-18 18:39:20 -04:00
7930cc8f31 Bump version to 1.7.6 2017-07-18 17:52:21 -04:00
526830ba61 Merge branch 'hotfix/1.7.5' 2017-06-10 18:56:22 -04:00
fe1513bb64 Fix race conditions on SQLiteScoreList 2017-06-10 16:48:53 -04:00
e06ace9ea8 Fix ArrayIndexOutOfBoundsException on FrequencyChart 2017-06-10 15:38:21 -04:00
d727dabb2b Fix snooze button on notifications 2017-06-10 15:30:51 -04:00
d17e8fcbfb Bump version to 1.7.5 2017-06-10 15:28:12 -04:00
102 changed files with 5688 additions and 991 deletions

1
.gitignore vendored
View File

@@ -22,3 +22,4 @@ gen/
local.properties
crowdin.yaml
local
secret/

View File

@@ -1,5 +1,30 @@
# Changelog
### 1.7.11 (Aug 10, 2019)
* Fix bug that produced corrupted CSV files in some countries
### 1.7.10 (June 15, 2019)
* Fix bug that prevented some devices from showing notifications.
* Update targetSdk to Android Pie (API level 28)
### 1.7.8 (April 21, 2018)
* Add support for adaptive icons (Oreo)
* Add support for notification channels (Oreo)
* Update translations
### 1.7.7 (September 30, 2017)
* Fix bug that caused reminders to show repeatedly on DST changes
### 1.7.6 (July 18, 2017)
* Fix bug that caused widgets not to render sometimes
* Fix other minor bugs
* Update translations
### 1.7.3 (May 30, 2017)
* Improve performance of 'sort by score'

View File

@@ -2,27 +2,47 @@ apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'jacoco'
apply plugin: 'com.github.triplet.play'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
compileSdkVersion 28
buildToolsVersion "28.0.3"
// signingConfigs {
// release {
// storeFile file(LOOP_STORE_FILE)
// storePassword LOOP_STORE_PASSWORD
// keyAlias LOOP_KEY_ALIAS
// keyPassword LOOP_KEY_PASSWORD
// }
// }
playAccountConfigs {
defaultAccountConfig {
jsonFile = file('../secret/playstore.json')
}
}
defaultConfig {
applicationId "org.isoron.uhabits"
minSdkVersion 15
targetSdkVersion 25
targetSdkVersion 28
buildConfigField "Integer", "databaseVersion", "15"
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArgument "size", "medium"
// playAccountConfig = playAccountConfigs.defaultAccountConfig
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
// signingConfig signingConfigs.release
}
debug {
testCoverageEnabled = false
@@ -53,7 +73,7 @@ dependencies {
androidTestApt 'com.google.dagger:dagger-compiler:2.2'
androidTestCompile 'com.android.support:support-annotations:25.3.0'
androidTestCompile 'com.android.support:support-annotations:27.1.1'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.google.auto.factory:auto-factory:1.0-beta3'
@@ -64,16 +84,15 @@ dependencies {
apt 'com.google.dagger:dagger-compiler:2.2'
apt 'com.jakewharton:butterknife-compiler:8.0.1'
compile 'com.android.support:appcompat-v7:25.3.0'
compile 'com.android.support:design:25.3.0'
compile 'com.android.support:preference-v14:25.3.0'
compile 'com.android.support:support-v4:25.3.0'
compile 'com.android.support:appcompat-v7:27.1.1'
compile 'com.android.support:design:27.1.1'
compile 'com.android.support:preference-v14:27.1.1'
compile 'com.android.support:support-v4:27.1.1'
compile 'com.getpebble:pebblekit:3.0.0'
compile 'com.github.paolorotolo:appintro:3.4.0'
compile 'com.google.auto.factory:auto-factory:1.0-beta3'
compile 'com.google.dagger:dagger:2.2'
compile 'com.jakewharton:butterknife:8.0.1'
compile 'com.michaelpardo:activeandroid:3.1.0-SNAPSHOT'
compile 'com.opencsv:opencsv:3.7'
compile 'org.apmem.tools:layouts:1.10@aar'
compile 'org.jetbrains:annotations-java5:15.0'
@@ -140,3 +159,7 @@ task coverageReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) {
classDirectories = files(fileTree(dir: classDir, excludes: excludes))
executionData = files(jvmExecData, connectedExecData)
}
//play {
// track = 'alpha'
//}

View File

@@ -67,6 +67,8 @@ public class BaseAndroidTest
protected ModelFactory modelFactory;
private boolean isDone = false;
@Before
public void setUp()
{
@@ -117,6 +119,25 @@ public class BaseAndroidTest
assertTrue(latch.await(60, TimeUnit.SECONDS));
}
protected void runConcurrently(Runnable... runnableList) throws Exception
{
isDone = false;
ExecutorService executor = Executors.newFixedThreadPool(100);
List<Future> futures = new LinkedList<>();
for (Runnable r : runnableList)
futures.add(executor.submit(() ->
{
while (!isDone) r.run();
return null;
}));
Thread.sleep(3000);
isDone = true;
executor.shutdown();
for(Future f : futures) f.get();
while (!executor.isTerminated()) Thread.sleep(50);
}
protected void setTheme(@StyleRes int themeId)
{
targetContext.setTheme(themeId);

View File

@@ -171,13 +171,13 @@ public class MainTest
clickMenuItem(R.string.archive);
assertHabitsDontExist(names);
clickMenuItem(R.string.show_archived);
clickMenuItem(R.string.hide_archived);
assertHabitsExist(names);
selectHabits(names);
clickMenuItem(R.string.unarchive);
clickMenuItem(R.string.show_archived);
clickMenuItem(R.string.hide_archived);
assertHabitsExist(names);
deleteHabits(names);

View File

@@ -115,6 +115,41 @@ public class SQLiteCheckmarkListTest extends BaseAndroidTest
assertThat(records.get(0).timestamp, equalTo(today - 21 * day));
}
@Test
public void testFixRecords() throws Exception
{
long day = DateUtils.millisecondsInOneDay;
long from = DateUtils.getStartOfToday();
long to = from + 5 * day;
List<CheckmarkRecord> original, actual, expected;
HabitRecord habit = new HabitRecord();
original = new ArrayList<>();
original.add(new CheckmarkRecord(habit, from + 8*day, 2));
original.add(new CheckmarkRecord(habit, from + 5*day, 0));
original.add(new CheckmarkRecord(habit, from + 4*day, 0));
original.add(new CheckmarkRecord(habit, from + 4*day, 2));
original.add(new CheckmarkRecord(habit, from + 3*day, 2));
original.add(new CheckmarkRecord(habit, from + 2*day, 1));
original.add(new CheckmarkRecord(habit, from + 2*day + 100, 1));
original.add(new CheckmarkRecord(habit, from, 0));
original.add(new CheckmarkRecord(habit, from, 2));
original.add(new CheckmarkRecord(habit, from - day, 2));
actual = SQLiteCheckmarkList.fixRecords(original, habit, from, to);
expected = new ArrayList<>();
expected.add(new CheckmarkRecord(habit, from + 5*day, 0));
expected.add(new CheckmarkRecord(habit, from + 4*day, 2));
expected.add(new CheckmarkRecord(habit, from + 3*day, 2));
expected.add(new CheckmarkRecord(habit, from + 2*day, 1));
expected.add(new CheckmarkRecord(habit, from + day, 0));
expected.add(new CheckmarkRecord(habit, from, 2));
assertThat(actual, equalTo(expected));
}
private List<CheckmarkRecord> getAllRecords()
{
return new Select()

View File

@@ -61,29 +61,6 @@ public class SQLiteScoreListTest extends BaseAndroidTest
day = DateUtils.millisecondsInOneDay;
}
@Test
public void testGetAll()
{
List<Score> list = scores.toList();
assertThat(list.size(), equalTo(121));
assertThat(list.get(0).getTimestamp(), equalTo(today));
assertThat(list.get(10).getTimestamp(), equalTo(today - 10 * day));
}
@Test
public void testInvalidateNewerThan()
{
scores.getTodayValue(); // force recompute
List<ScoreRecord> records = getAllRecords();
assertThat(records.size(), equalTo(121));
scores.invalidateNewerThan(today - 10 * day);
records = getAllRecords();
assertThat(records.size(), equalTo(110));
assertThat(records.get(0).timestamp, equalTo(today - 11 * day));
}
@Test
public void testAdd()
{
@@ -101,6 +78,15 @@ public class SQLiteScoreListTest extends BaseAndroidTest
assertThat(records.get(0).timestamp, equalTo(today));
}
@Test
public void testGetAll()
{
List<Score> list = scores.toList();
assertThat(list.size(), equalTo(121));
assertThat(list.get(0).getTimestamp(), equalTo(today));
assertThat(list.get(10).getTimestamp(), equalTo(today - 10 * day));
}
@Test
public void testGetByInterval()
{
@@ -115,6 +101,16 @@ public class SQLiteScoreListTest extends BaseAndroidTest
assertThat(list.get(7).getTimestamp(), equalTo(today - 10 * day));
}
@Test
public void testGetByInterval_concurrent() throws Exception
{
Runnable block1 = () -> scores.invalidateNewerThan(0);
Runnable block2 =
() -> assertThat(scores.getByInterval(today, today).size(),
equalTo(1));
runConcurrently(block1, block2);
}
@Test
public void testGetByInterval_withLongInterval()
{
@@ -125,6 +121,30 @@ public class SQLiteScoreListTest extends BaseAndroidTest
assertThat(list.size(), equalTo(201));
}
@Test
public void testGetTodayValue_concurrent() throws Exception
{
Runnable block1 = () -> scores.invalidateNewerThan(0);
Runnable block2 =
() -> assertThat(scores.getTodayValue(), equalTo(18407827));
runConcurrently(block1, block2);
}
@Test
public void testInvalidateNewerThan()
{
scores.getTodayValue(); // force recompute
List<ScoreRecord> records = getAllRecords();
assertThat(records.size(), equalTo(121));
scores.invalidateNewerThan(today - 10 * day);
records = getAllRecords();
assertThat(records.size(), equalTo(110));
assertThat(records.get(0).timestamp, equalTo(today - 11 * day));
}
private List<ScoreRecord> getAllRecords()
{
return new Select()

View File

@@ -1,173 +0,0 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
* Loop Habit Tracker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Loop Habit Tracker is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.pebble;
import android.content.*;
import android.support.annotation.*;
import android.support.test.runner.*;
import android.test.suitebuilder.annotation.*;
import com.getpebble.android.kit.*;
import com.getpebble.android.kit.util.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.receivers.*;
import org.json.*;
import org.junit.*;
import org.junit.runner.*;
import static com.getpebble.android.kit.Constants.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.core.IsEqual.*;
@RunWith(AndroidJUnit4.class)
@MediumTest
public class PebbleReceiverTest extends BaseAndroidTest
{
private Habit habit1;
private Habit habit2;
@Override
public void setUp()
{
super.setUp();
fixtures.purgeHabits(habitList);
habit1 = fixtures.createEmptyHabit();
habit1.setName("Exercise");
habit2 = fixtures.createEmptyHabit();
habit2.setName("Meditate");
}
@Test
public void testCount() throws Exception
{
onPebbleReceived((dict) -> {
assertThat(dict.getString(0), equalTo("COUNT"));
assertThat(dict.getInteger(1), equalTo(2L));
});
PebbleDictionary dict = buildCountRequest();
sendFromPebbleToAndroid(dict);
awaitLatch();
}
@Test
public void testFetch() throws Exception
{
onPebbleReceived((dict) -> {
assertThat(dict.getString(0), equalTo("HABIT"));
assertThat(dict.getInteger(1), equalTo(habit2.getId()));
assertThat(dict.getString(2), equalTo(habit2.getName()));
assertThat(dict.getInteger(3), equalTo(0L));
});
PebbleDictionary dict = buildFetchRequest(1);
sendFromPebbleToAndroid(dict);
awaitLatch();
}
// @Test
// public void testToggle() throws Exception
// {
// int v = habit1.getCheckmarks().getTodayValue();
// assertThat(v, equalTo(Checkmark.UNCHECKED));
//
// onPebbleReceived((dict) -> {
// assertThat(dict.getString(0), equalTo("OK"));
// int value = habit1.getCheckmarks().getTodayValue();
// assertThat(value, equalTo(200)); //Checkmark.CHECKED_EXPLICITLY));
// });
//
// PebbleDictionary dict = buildToggleRequest(habit1.getId());
// sendFromPebbleToAndroid(dict);
// awaitLatch();
// }
@NonNull
protected PebbleDictionary buildCountRequest()
{
PebbleDictionary dict = new PebbleDictionary();
dict.addString(0, "COUNT");
return dict;
}
@NonNull
protected PebbleDictionary buildFetchRequest(int position)
{
PebbleDictionary dict = new PebbleDictionary();
dict.addString(0, "FETCH");
dict.addInt32(1, position);
return dict;
}
protected void onPebbleReceived(PebbleProcessor processor)
{
BroadcastReceiver pebbleReceiver = new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent)
{
try
{
String jsonData = intent.getStringExtra(MSG_DATA);
PebbleDictionary dict = PebbleDictionary.fromJson(jsonData);
processor.process(dict);
latch.countDown();
targetContext.unregisterReceiver(this);
}
catch (JSONException e)
{
throw new RuntimeException(e);
}
}
};
IntentFilter filter = new IntentFilter(Constants.INTENT_APP_SEND);
targetContext.registerReceiver(pebbleReceiver, filter);
}
protected void sendFromPebbleToAndroid(PebbleDictionary dict)
{
Intent intent = new Intent(Constants.INTENT_APP_RECEIVE);
intent.putExtra(Constants.APP_UUID, PebbleReceiver.WATCHAPP_UUID);
intent.putExtra(Constants.TRANSACTION_ID, 0);
intent.putExtra(Constants.MSG_DATA, dict.toJsonString());
targetContext.sendBroadcast(intent);
}
private PebbleDictionary buildToggleRequest(long habitId)
{
PebbleDictionary dict = new PebbleDictionary();
dict.addString(0, "TOGGLE");
dict.addInt32(1, (int) habitId);
return dict;
}
interface PebbleProcessor
{
void process(PebbleDictionary dict);
}
}

View File

@@ -21,8 +21,8 @@
<manifest
package="org.isoron.uhabits"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="31"
android:versionName="1.7.4">
android:versionCode="38"
android:versionName="1.7.11">
<uses-permission android:name="android.permission.VIBRATE"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@@ -0,0 +1,86 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import com.activeandroid.util.Log;
public final class ActiveAndroid {
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static void initialize(Context context) {
initialize(new Configuration.Builder(context).create());
}
public static void initialize(Configuration configuration) {
initialize(configuration, false);
}
public static void initialize(Context context, boolean loggingEnabled) {
initialize(new Configuration.Builder(context).create(), loggingEnabled);
}
public static void initialize(Configuration configuration, boolean loggingEnabled) {
// Set logging enabled first
setLoggingEnabled(loggingEnabled);
Cache.initialize(configuration);
}
public static void clearCache() {
Cache.clear();
}
public static void dispose() {
Cache.dispose();
}
public static void setLoggingEnabled(boolean enabled) {
Log.setEnabled(enabled);
}
public static SQLiteDatabase getDatabase() {
return Cache.openDatabase();
}
public static void beginTransaction() {
Cache.openDatabase().beginTransaction();
}
public static void endTransaction() {
Cache.openDatabase().endTransaction();
}
public static void setTransactionSuccessful() {
Cache.openDatabase().setTransactionSuccessful();
}
public static boolean inTransaction() {
return Cache.openDatabase().inTransaction();
}
public static void execSQL(String sql) {
Cache.openDatabase().execSQL(sql);
}
public static void execSQL(String sql, Object[] bindArgs) {
Cache.openDatabase().execSQL(sql, bindArgs);
}
}

View File

@@ -0,0 +1,158 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.util.Collection;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.support.v4.util.LruCache;
import com.activeandroid.serializer.TypeSerializer;
import com.activeandroid.util.Log;
public final class Cache {
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC CONSTANTS
//////////////////////////////////////////////////////////////////////////////////////
public static final int DEFAULT_CACHE_SIZE = 1024;
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private static Context sContext;
private static ModelInfo sModelInfo;
private static DatabaseHelper sDatabaseHelper;
private static LruCache<String, Model> sEntities;
private static boolean sIsInitialized = false;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
private Cache() {
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static synchronized void initialize(Configuration configuration) {
if (sIsInitialized) {
Log.v("ActiveAndroid already initialized.");
return;
}
sContext = configuration.getContext();
sModelInfo = new ModelInfo(configuration);
sDatabaseHelper = new DatabaseHelper(configuration);
// TODO: It would be nice to override sizeOf here and calculate the memory
// actually used, however at this point it seems like the reflection
// required would be too costly to be of any benefit. We'll just set a max
// object size instead.
sEntities = new LruCache<String, Model>(configuration.getCacheSize());
openDatabase();
sIsInitialized = true;
Log.v("ActiveAndroid initialized successfully.");
}
public static synchronized void clear() {
sEntities.evictAll();
Log.v("Cache cleared.");
}
public static synchronized void dispose() {
closeDatabase();
sEntities = null;
sModelInfo = null;
sDatabaseHelper = null;
sIsInitialized = false;
Log.v("ActiveAndroid disposed. Call initialize to use library.");
}
// Database access
public static boolean isInitialized() {
return sIsInitialized;
}
public static synchronized SQLiteDatabase openDatabase() {
return sDatabaseHelper.getWritableDatabase();
}
public static synchronized void closeDatabase() {
sDatabaseHelper.close();
}
// Context access
public static Context getContext() {
return sContext;
}
// Entity cache
public static String getIdentifier(Class<? extends Model> type, Long id) {
return getTableName(type) + "@" + id;
}
public static String getIdentifier(Model entity) {
return getIdentifier(entity.getClass(), entity.getId());
}
public static synchronized void addEntity(Model entity) {
sEntities.put(getIdentifier(entity), entity);
}
public static synchronized Model getEntity(Class<? extends Model> type, long id) {
return sEntities.get(getIdentifier(type, id));
}
public static synchronized void removeEntity(Model entity) {
sEntities.remove(getIdentifier(entity));
}
// Model cache
public static synchronized Collection<TableInfo> getTableInfos() {
return sModelInfo.getTableInfos();
}
public static synchronized TableInfo getTableInfo(Class<? extends Model> type) {
return sModelInfo.getTableInfo(type);
}
public static synchronized TypeSerializer getParserForType(Class<?> type) {
return sModelInfo.getTypeSerializer(type);
}
public static synchronized String getTableName(Class<? extends Model> type) {
return sModelInfo.getTableInfo(type).getTableName();
}
}

View File

@@ -0,0 +1,318 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.content.Context;
import com.activeandroid.serializer.TypeSerializer;
import com.activeandroid.util.Log;
import com.activeandroid.util.ReflectionUtils;
public class Configuration {
public final static String SQL_PARSER_LEGACY = "legacy";
public final static String SQL_PARSER_DELIMITED = "delimited";
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private Context mContext;
private String mDatabaseName;
private int mDatabaseVersion;
private String mSqlParser;
private List<Class<? extends Model>> mModelClasses;
private List<Class<? extends TypeSerializer>> mTypeSerializers;
private int mCacheSize;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
private Configuration(Context context) {
mContext = context;
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public Context getContext() {
return mContext;
}
public String getDatabaseName() {
return mDatabaseName;
}
public int getDatabaseVersion() {
return mDatabaseVersion;
}
public String getSqlParser() {
return mSqlParser;
}
public List<Class<? extends Model>> getModelClasses() {
return mModelClasses;
}
public List<Class<? extends TypeSerializer>> getTypeSerializers() {
return mTypeSerializers;
}
public int getCacheSize() {
return mCacheSize;
}
public boolean isValid() {
return mModelClasses != null && mModelClasses.size() > 0;
}
//////////////////////////////////////////////////////////////////////////////////////
// INNER CLASSES
//////////////////////////////////////////////////////////////////////////////////////
public static class Builder {
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE CONSTANTS
//////////////////////////////////////////////////////////////////////////////////////
private static final String AA_DB_NAME = "AA_DB_NAME";
private static final String AA_DB_VERSION = "AA_DB_VERSION";
private final static String AA_MODELS = "AA_MODELS";
private final static String AA_SERIALIZERS = "AA_SERIALIZERS";
private final static String AA_SQL_PARSER = "AA_SQL_PARSER";
private static final int DEFAULT_CACHE_SIZE = 1024;
private static final String DEFAULT_DB_NAME = "Application.db";
private static final String DEFAULT_SQL_PARSER = SQL_PARSER_LEGACY;
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private Context mContext;
private Integer mCacheSize;
private String mDatabaseName;
private Integer mDatabaseVersion;
private String mSqlParser;
private List<Class<? extends Model>> mModelClasses;
private List<Class<? extends TypeSerializer>> mTypeSerializers;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
public Builder(Context context) {
mContext = context.getApplicationContext();
mCacheSize = DEFAULT_CACHE_SIZE;
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public Builder setCacheSize(int cacheSize) {
mCacheSize = cacheSize;
return this;
}
public Builder setDatabaseName(String databaseName) {
mDatabaseName = databaseName;
return this;
}
public Builder setDatabaseVersion(int databaseVersion) {
mDatabaseVersion = databaseVersion;
return this;
}
public Builder setSqlParser(String sqlParser) {
mSqlParser = sqlParser;
return this;
}
public Builder addModelClass(Class<? extends Model> modelClass) {
if (mModelClasses == null) {
mModelClasses = new ArrayList<Class<? extends Model>>();
}
mModelClasses.add(modelClass);
return this;
}
public Builder addModelClasses(Class<? extends Model>... modelClasses) {
if (mModelClasses == null) {
mModelClasses = new ArrayList<Class<? extends Model>>();
}
mModelClasses.addAll(Arrays.asList(modelClasses));
return this;
}
public Builder setModelClasses(Class<? extends Model>... modelClasses) {
mModelClasses = Arrays.asList(modelClasses);
return this;
}
public Builder addTypeSerializer(Class<? extends TypeSerializer> typeSerializer) {
if (mTypeSerializers == null) {
mTypeSerializers = new ArrayList<Class<? extends TypeSerializer>>();
}
mTypeSerializers.add(typeSerializer);
return this;
}
public Builder addTypeSerializers(Class<? extends TypeSerializer>... typeSerializers) {
if (mTypeSerializers == null) {
mTypeSerializers = new ArrayList<Class<? extends TypeSerializer>>();
}
mTypeSerializers.addAll(Arrays.asList(typeSerializers));
return this;
}
public Builder setTypeSerializers(Class<? extends TypeSerializer>... typeSerializers) {
mTypeSerializers = Arrays.asList(typeSerializers);
return this;
}
public Configuration create() {
Configuration configuration = new Configuration(mContext);
configuration.mCacheSize = mCacheSize;
// Get database name from meta-data
if (mDatabaseName != null) {
configuration.mDatabaseName = mDatabaseName;
} else {
configuration.mDatabaseName = getMetaDataDatabaseNameOrDefault();
}
// Get database version from meta-data
if (mDatabaseVersion != null) {
configuration.mDatabaseVersion = mDatabaseVersion;
} else {
configuration.mDatabaseVersion = getMetaDataDatabaseVersionOrDefault();
}
// Get SQL parser from meta-data
if (mSqlParser != null) {
configuration.mSqlParser = mSqlParser;
} else {
configuration.mSqlParser = getMetaDataSqlParserOrDefault();
}
// Get model classes from meta-data
if (mModelClasses != null) {
configuration.mModelClasses = mModelClasses;
} else {
final String modelList = ReflectionUtils.getMetaData(mContext, AA_MODELS);
if (modelList != null) {
configuration.mModelClasses = loadModelList(modelList.split(","));
}
}
// Get type serializer classes from meta-data
if (mTypeSerializers != null) {
configuration.mTypeSerializers = mTypeSerializers;
} else {
final String serializerList = ReflectionUtils.getMetaData(mContext, AA_SERIALIZERS);
if (serializerList != null) {
configuration.mTypeSerializers = loadSerializerList(serializerList.split(","));
}
}
return configuration;
}
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
// Meta-data methods
private String getMetaDataDatabaseNameOrDefault() {
String aaName = ReflectionUtils.getMetaData(mContext, AA_DB_NAME);
if (aaName == null) {
aaName = DEFAULT_DB_NAME;
}
return aaName;
}
private int getMetaDataDatabaseVersionOrDefault() {
Integer aaVersion = ReflectionUtils.getMetaData(mContext, AA_DB_VERSION);
if (aaVersion == null || aaVersion == 0) {
aaVersion = 1;
}
return aaVersion;
}
private String getMetaDataSqlParserOrDefault() {
final String mode = ReflectionUtils.getMetaData(mContext, AA_SQL_PARSER);
if (mode == null) {
return DEFAULT_SQL_PARSER;
}
return mode;
}
private List<Class<? extends Model>> loadModelList(String[] models) {
final List<Class<? extends Model>> modelClasses = new ArrayList<Class<? extends Model>>();
final ClassLoader classLoader = mContext.getClass().getClassLoader();
for (String model : models) {
try {
Class modelClass = Class.forName(model.trim(), false, classLoader);
if (ReflectionUtils.isModel(modelClass)) {
modelClasses.add(modelClass);
}
}
catch (ClassNotFoundException e) {
Log.e("Couldn't create class.", e);
}
}
return modelClasses;
}
private List<Class<? extends TypeSerializer>> loadSerializerList(String[] serializers) {
final List<Class<? extends TypeSerializer>> typeSerializers = new ArrayList<Class<? extends TypeSerializer>>();
final ClassLoader classLoader = mContext.getClass().getClassLoader();
for (String serializer : serializers) {
try {
Class serializerClass = Class.forName(serializer.trim(), false, classLoader);
if (ReflectionUtils.isTypeSerializer(serializerClass)) {
typeSerializers.add(serializerClass);
}
}
catch (ClassNotFoundException e) {
Log.e("Couldn't create class.", e);
}
}
return typeSerializers;
}
}
}

View File

@@ -0,0 +1,257 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import com.activeandroid.util.IOUtils;
import com.activeandroid.util.Log;
import com.activeandroid.util.NaturalOrderComparator;
import com.activeandroid.util.SQLiteUtils;
import com.activeandroid.util.SqlParser;
public final class DatabaseHelper extends SQLiteOpenHelper {
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC CONSTANTS
//////////////////////////////////////////////////////////////////////////////////////
public final static String MIGRATION_PATH = "migrations";
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE FIELDS
//////////////////////////////////////////////////////////////////////////////////////
private final String mSqlParser;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
public DatabaseHelper(Configuration configuration) {
super(configuration.getContext(), configuration.getDatabaseName(), null, configuration.getDatabaseVersion());
copyAttachedDatabase(configuration.getContext(), configuration.getDatabaseName());
mSqlParser = configuration.getSqlParser();
}
//////////////////////////////////////////////////////////////////////////////////////
// OVERRIDEN METHODS
//////////////////////////////////////////////////////////////////////////////////////
@Override
public void onOpen(SQLiteDatabase db) {
executePragmas(db);
};
@Override
public void onCreate(SQLiteDatabase db) {
executePragmas(db);
executeCreate(db);
executeMigrations(db, -1, db.getVersion());
executeCreateIndex(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
executePragmas(db);
executeCreate(db);
executeMigrations(db, oldVersion, newVersion);
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public void copyAttachedDatabase(Context context, String databaseName) {
final File dbPath = context.getDatabasePath(databaseName);
// If the database already exists, return
if (dbPath.exists()) {
return;
}
// Make sure we have a path to the file
dbPath.getParentFile().mkdirs();
// Try to copy database file
try {
final InputStream inputStream = context.getAssets().open(databaseName);
final OutputStream output = new FileOutputStream(dbPath);
byte[] buffer = new byte[8192];
int length;
while ((length = inputStream.read(buffer, 0, 8192)) > 0) {
output.write(buffer, 0, length);
}
output.flush();
output.close();
inputStream.close();
}
catch (IOException e) {
Log.e("Failed to open file", e);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
private void executePragmas(SQLiteDatabase db) {
if (SQLiteUtils.FOREIGN_KEYS_SUPPORTED) {
db.execSQL("PRAGMA foreign_keys=ON;");
Log.i("Foreign Keys supported. Enabling foreign key features.");
}
}
private void executeCreateIndex(SQLiteDatabase db) {
db.beginTransaction();
try {
for (TableInfo tableInfo : Cache.getTableInfos()) {
String[] definitions = SQLiteUtils.createIndexDefinition(tableInfo);
for (String definition : definitions) {
db.execSQL(definition);
}
}
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
}
private void executeCreate(SQLiteDatabase db) {
db.beginTransaction();
try {
for (TableInfo tableInfo : Cache.getTableInfos()) {
db.execSQL(SQLiteUtils.createTableDefinition(tableInfo));
}
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
}
private boolean executeMigrations(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean migrationExecuted = false;
try {
final List<String> files = Arrays.asList(Cache.getContext().getAssets().list(MIGRATION_PATH));
Collections.sort(files, new NaturalOrderComparator());
db.beginTransaction();
try {
for (String file : files) {
try {
final int version = Integer.valueOf(file.replace(".sql", ""));
if (version > oldVersion && version <= newVersion) {
executeSqlScript(db, file);
migrationExecuted = true;
Log.i(file + " executed succesfully.");
}
}
catch (NumberFormatException e) {
Log.w("Skipping invalidly named file: " + file, e);
}
}
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
}
catch (IOException e) {
Log.e("Failed to execute migrations.", e);
}
return migrationExecuted;
}
private void executeSqlScript(SQLiteDatabase db, String file) {
InputStream stream = null;
try {
stream = Cache.getContext().getAssets().open(MIGRATION_PATH + "/" + file);
if (Configuration.SQL_PARSER_DELIMITED.equalsIgnoreCase(mSqlParser)) {
executeDelimitedSqlScript(db, stream);
} else {
executeLegacySqlScript(db, stream);
}
} catch (IOException e) {
Log.e("Failed to execute " + file, e);
} finally {
IOUtils.closeQuietly(stream);
}
}
private void executeDelimitedSqlScript(SQLiteDatabase db, InputStream stream) throws IOException {
List<String> commands = SqlParser.parse(stream);
for(String command : commands) {
db.execSQL(command);
}
}
private void executeLegacySqlScript(SQLiteDatabase db, InputStream stream) throws IOException {
InputStreamReader reader = null;
BufferedReader buffer = null;
try {
reader = new InputStreamReader(stream);
buffer = new BufferedReader(reader);
String line = null;
while ((line = buffer.readLine()) != null) {
line = line.replace(";", "").trim();
if (!TextUtils.isEmpty(line)) {
db.execSQL(line);
}
}
} finally {
IOUtils.closeQuietly(buffer);
IOUtils.closeQuietly(reader);
}
}
}

View File

@@ -0,0 +1,314 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.activeandroid.query.Delete;
import com.activeandroid.query.Select;
import com.activeandroid.serializer.TypeSerializer;
import com.activeandroid.util.Log;
import com.activeandroid.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@SuppressWarnings("unchecked")
public abstract class Model {
/** Prime number used for hashcode() implementation. */
private static final int HASH_PRIME = 739;
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private Long mId = null;
private final TableInfo mTableInfo;
private final String idName;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
public Model() {
mTableInfo = Cache.getTableInfo(getClass());
idName = mTableInfo.getIdName();
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public final Long getId() {
return mId;
}
public final void delete() {
Cache.openDatabase().delete(mTableInfo.getTableName(), idName+"=?", new String[] { getId().toString() });
Cache.removeEntity(this);
}
public final Long save() {
final SQLiteDatabase db = Cache.openDatabase();
final ContentValues values = new ContentValues();
for (Field field : mTableInfo.getFields()) {
final String fieldName = mTableInfo.getColumnName(field);
Class<?> fieldType = field.getType();
field.setAccessible(true);
try {
Object value = field.get(this);
if (value != null) {
final TypeSerializer typeSerializer = Cache.getParserForType(fieldType);
if (typeSerializer != null) {
// serialize data
value = typeSerializer.serialize(value);
// set new object type
if (value != null) {
fieldType = value.getClass();
// check that the serializer returned what it promised
if (!fieldType.equals(typeSerializer.getSerializedType())) {
Log.w(String.format("TypeSerializer returned wrong type: expected a %s but got a %s",
typeSerializer.getSerializedType(), fieldType));
}
}
}
}
// TODO: Find a smarter way to do this? This if block is necessary because we
// can't know the type until runtime.
if (value == null) {
values.putNull(fieldName);
}
else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
values.put(fieldName, (Byte) value);
}
else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
values.put(fieldName, (Short) value);
}
else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
values.put(fieldName, (Integer) value);
}
else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
values.put(fieldName, (Long) value);
}
else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
values.put(fieldName, (Float) value);
}
else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
values.put(fieldName, (Double) value);
}
else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) {
values.put(fieldName, (Boolean) value);
}
else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) {
values.put(fieldName, value.toString());
}
else if (fieldType.equals(String.class)) {
values.put(fieldName, value.toString());
}
else if (fieldType.equals(Byte[].class) || fieldType.equals(byte[].class)) {
values.put(fieldName, (byte[]) value);
}
else if (ReflectionUtils.isModel(fieldType)) {
values.put(fieldName, ((Model) value).getId());
}
else if (ReflectionUtils.isSubclassOf(fieldType, Enum.class)) {
values.put(fieldName, ((Enum<?>) value).name());
}
}
catch (IllegalArgumentException e) {
Log.e(e.getClass().getName(), e);
}
catch (IllegalAccessException e) {
Log.e(e.getClass().getName(), e);
}
}
if (mId == null) {
mId = db.insert(mTableInfo.getTableName(), null, values);
}
else {
db.update(mTableInfo.getTableName(), values, idName+"=" + mId, null);
}
return mId;
}
// Convenience methods
public static void delete(Class<? extends Model> type, long id) {
TableInfo tableInfo = Cache.getTableInfo(type);
new Delete().from(type).where(tableInfo.getIdName()+"=?", id).execute();
}
public static <T extends Model> T load(Class<T> type, long id) {
TableInfo tableInfo = Cache.getTableInfo(type);
return (T) new Select().from(type).where(tableInfo.getIdName()+"=?", id).executeSingle();
}
// Model population
public final void loadFromCursor(Cursor cursor) {
/**
* Obtain the columns ordered to fix issue #106 (https://github.com/pardom/ActiveAndroid/issues/106)
* when the cursor have multiple columns with same name obtained from join tables.
*/
List<String> columnsOrdered = new ArrayList<String>(Arrays.asList(cursor.getColumnNames()));
for (Field field : mTableInfo.getFields()) {
final String fieldName = mTableInfo.getColumnName(field);
Class<?> fieldType = field.getType();
final int columnIndex = columnsOrdered.indexOf(fieldName);
if (columnIndex < 0) {
continue;
}
field.setAccessible(true);
try {
boolean columnIsNull = cursor.isNull(columnIndex);
TypeSerializer typeSerializer = Cache.getParserForType(fieldType);
Object value = null;
if (typeSerializer != null) {
fieldType = typeSerializer.getSerializedType();
}
// TODO: Find a smarter way to do this? This if block is necessary because we
// can't know the type until runtime.
if (columnIsNull) {
field = null;
}
else if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) {
value = cursor.getInt(columnIndex);
}
else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) {
value = cursor.getInt(columnIndex);
}
else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) {
value = cursor.getInt(columnIndex);
}
else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) {
value = cursor.getLong(columnIndex);
}
else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) {
value = cursor.getFloat(columnIndex);
}
else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) {
value = cursor.getDouble(columnIndex);
}
else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) {
value = cursor.getInt(columnIndex) != 0;
}
else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) {
value = cursor.getString(columnIndex).charAt(0);
}
else if (fieldType.equals(String.class)) {
value = cursor.getString(columnIndex);
}
else if (fieldType.equals(Byte[].class) || fieldType.equals(byte[].class)) {
value = cursor.getBlob(columnIndex);
}
else if (ReflectionUtils.isModel(fieldType)) {
final long entityId = cursor.getLong(columnIndex);
final Class<? extends Model> entityType = (Class<? extends Model>) fieldType;
Model entity = Cache.getEntity(entityType, entityId);
if (entity == null) {
entity = new Select().from(entityType).where(idName+"=?", entityId).executeSingle();
}
value = entity;
}
else if (ReflectionUtils.isSubclassOf(fieldType, Enum.class)) {
@SuppressWarnings("rawtypes")
final Class<? extends Enum> enumType = (Class<? extends Enum>) fieldType;
value = Enum.valueOf(enumType, cursor.getString(columnIndex));
}
// Use a deserializer if one is available
if (typeSerializer != null && !columnIsNull) {
value = typeSerializer.deserialize(value);
}
// Set the field value
if (value != null) {
field.set(this, value);
}
}
catch (IllegalArgumentException e) {
Log.e(e.getClass().getName(), e);
}
catch (IllegalAccessException e) {
Log.e(e.getClass().getName(), e);
}
catch (SecurityException e) {
Log.e(e.getClass().getName(), e);
}
}
if (mId != null) {
Cache.addEntity(this);
}
}
//////////////////////////////////////////////////////////////////////////////////////
// PROTECTED METHODS
//////////////////////////////////////////////////////////////////////////////////////
protected final <T extends Model> List<T> getMany(Class<T> type, String foreignKey) {
return new Select().from(type).where(Cache.getTableName(type) + "." + foreignKey + "=?", getId()).execute();
}
//////////////////////////////////////////////////////////////////////////////////////
// OVERRIDEN METHODS
//////////////////////////////////////////////////////////////////////////////////////
@Override
public String toString() {
return mTableInfo.getTableName() + "@" + getId();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Model && this.mId != null) {
final Model other = (Model) obj;
return this.mId.equals(other.mId)
&& (this.mTableInfo.getTableName().equals(other.mTableInfo.getTableName()));
} else {
return this == obj;
}
}
@Override
public int hashCode() {
int hash = HASH_PRIME;
hash += HASH_PRIME * (mId == null ? super.hashCode() : mId.hashCode()); //if id is null, use Object.hashCode()
hash += HASH_PRIME * mTableInfo.getTableName().hashCode();
return hash; //To change body of generated methods, choose Tools | Templates.
}
}

View File

@@ -0,0 +1,209 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.content.Context;
import com.activeandroid.serializer.CalendarSerializer;
import com.activeandroid.serializer.SqlDateSerializer;
import com.activeandroid.serializer.TypeSerializer;
import com.activeandroid.serializer.UtilDateSerializer;
import com.activeandroid.serializer.FileSerializer;
import com.activeandroid.util.Log;
import com.activeandroid.util.ReflectionUtils;
import dalvik.system.DexFile;
final class ModelInfo {
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
private Map<Class<? extends Model>, TableInfo> mTableInfos = new HashMap<Class<? extends Model>, TableInfo>();
private Map<Class<?>, TypeSerializer> mTypeSerializers = new HashMap<Class<?>, TypeSerializer>() {
{
put(Calendar.class, new CalendarSerializer());
put(java.sql.Date.class, new SqlDateSerializer());
put(java.util.Date.class, new UtilDateSerializer());
put(java.io.File.class, new FileSerializer());
}
};
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
public ModelInfo(Configuration configuration) {
if (!loadModelFromMetaData(configuration)) {
try {
scanForModel(configuration.getContext());
}
catch (IOException e) {
Log.e("Couldn't open source path.", e);
}
}
Log.i("ModelInfo loaded.");
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public Collection<TableInfo> getTableInfos() {
return mTableInfos.values();
}
public TableInfo getTableInfo(Class<? extends Model> type) {
return mTableInfos.get(type);
}
public TypeSerializer getTypeSerializer(Class<?> type) {
return mTypeSerializers.get(type);
}
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
private boolean loadModelFromMetaData(Configuration configuration) {
if (!configuration.isValid()) {
return false;
}
final List<Class<? extends Model>> models = configuration.getModelClasses();
if (models != null) {
for (Class<? extends Model> model : models) {
mTableInfos.put(model, new TableInfo(model));
}
}
final List<Class<? extends TypeSerializer>> typeSerializers = configuration.getTypeSerializers();
if (typeSerializers != null) {
for (Class<? extends TypeSerializer> typeSerializer : typeSerializers) {
try {
TypeSerializer instance = typeSerializer.newInstance();
mTypeSerializers.put(instance.getDeserializedType(), instance);
}
catch (InstantiationException e) {
Log.e("Couldn't instantiate TypeSerializer.", e);
}
catch (IllegalAccessException e) {
Log.e("IllegalAccessException", e);
}
}
}
return true;
}
private void scanForModel(Context context) throws IOException {
String packageName = context.getPackageName();
String sourcePath = context.getApplicationInfo().sourceDir;
List<String> paths = new ArrayList<String>();
if (sourcePath != null && !(new File(sourcePath).isDirectory())) {
DexFile dexfile = new DexFile(sourcePath);
Enumeration<String> entries = dexfile.entries();
while (entries.hasMoreElements()) {
paths.add(entries.nextElement());
}
}
// Robolectric fallback
else {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources("");
while (resources.hasMoreElements()) {
String path = resources.nextElement().getFile();
if (path.contains("bin") || path.contains("classes")) {
paths.add(path);
}
}
}
for (String path : paths) {
File file = new File(path);
scanForModelClasses(file, packageName, context.getClassLoader());
}
}
private void scanForModelClasses(File path, String packageName, ClassLoader classLoader) {
if (path.isDirectory()) {
for (File file : path.listFiles()) {
scanForModelClasses(file, packageName, classLoader);
}
}
else {
String className = path.getName();
// Robolectric fallback
if (!path.getPath().equals(className)) {
className = path.getPath();
if (className.endsWith(".class")) {
className = className.substring(0, className.length() - 6);
}
else {
return;
}
className = className.replace(System.getProperty("file.separator"), ".");
int packageNameIndex = className.lastIndexOf(packageName);
if (packageNameIndex < 0) {
return;
}
className = className.substring(packageNameIndex);
}
try {
Class<?> discoveredClass = Class.forName(className, false, classLoader);
if (ReflectionUtils.isModel(discoveredClass)) {
@SuppressWarnings("unchecked")
Class<? extends Model> modelClass = (Class<? extends Model>) discoveredClass;
mTableInfos.put(modelClass, new TableInfo(modelClass));
}
else if (ReflectionUtils.isTypeSerializer(discoveredClass)) {
TypeSerializer instance = (TypeSerializer) discoveredClass.newInstance();
mTypeSerializers.put(instance.getDeserializedType(), instance);
}
}
catch (ClassNotFoundException e) {
Log.e("Couldn't create class.", e);
}
catch (InstantiationException e) {
Log.e("Couldn't instantiate TypeSerializer.", e);
}
catch (IllegalAccessException e) {
Log.e("IllegalAccessException", e);
}
}
}
}

View File

@@ -0,0 +1,124 @@
package com.activeandroid;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import android.text.TextUtils;
import android.util.Log;
import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Table;
import com.activeandroid.util.ReflectionUtils;
public final class TableInfo {
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private Class<? extends Model> mType;
private String mTableName;
private String mIdName = Table.DEFAULT_ID_NAME;
private Map<Field, String> mColumnNames = new LinkedHashMap<Field, String>();
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
public TableInfo(Class<? extends Model> type) {
mType = type;
final Table tableAnnotation = type.getAnnotation(Table.class);
if (tableAnnotation != null) {
mTableName = tableAnnotation.name();
mIdName = tableAnnotation.id();
}
else {
mTableName = type.getSimpleName();
}
// Manually add the id column since it is not declared like the other columns.
Field idField = getIdField(type);
mColumnNames.put(idField, mIdName);
List<Field> fields = new LinkedList<Field>(ReflectionUtils.getDeclaredColumnFields(type));
Collections.reverse(fields);
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
final Column columnAnnotation = field.getAnnotation(Column.class);
String columnName = columnAnnotation.name();
if (TextUtils.isEmpty(columnName)) {
columnName = field.getName();
}
mColumnNames.put(field, columnName);
}
}
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public Class<? extends Model> getType() {
return mType;
}
public String getTableName() {
return mTableName;
}
public String getIdName() {
return mIdName;
}
public Collection<Field> getFields() {
return mColumnNames.keySet();
}
public String getColumnName(Field field) {
return mColumnNames.get(field);
}
private Field getIdField(Class<?> type) {
if (type.equals(Model.class)) {
try {
return type.getDeclaredField("mId");
}
catch (NoSuchFieldException e) {
Log.e("Impossible!", e.toString());
}
}
else if (type.getSuperclass() != null) {
return getIdField(type.getSuperclass());
}
return null;
}
}

View File

@@ -0,0 +1,110 @@
package com.activeandroid.annotation;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
public enum ConflictAction {
ROLLBACK, ABORT, FAIL, IGNORE, REPLACE
}
public enum ForeignKeyAction {
SET_NULL, SET_DEFAULT, CASCADE, RESTRICT, NO_ACTION
}
public String name() default "";
public int length() default -1;
public boolean notNull() default false;
public ConflictAction onNullConflict() default ConflictAction.FAIL;
public ForeignKeyAction onDelete() default ForeignKeyAction.NO_ACTION;
public ForeignKeyAction onUpdate() default ForeignKeyAction.NO_ACTION;
public boolean unique() default false;
public ConflictAction onUniqueConflict() default ConflictAction.FAIL;
/*
* If set uniqueGroups = {"group_name"}, we will create a table constraint with group.
*
* Example:
*
* @Table(name = "table_name")
* public class Table extends Model {
* @Column(name = "member1", uniqueGroups = {"group1"}, onUniqueConflicts = {ConflictAction.FAIL})
* public String member1;
*
* @Column(name = "member2", uniqueGroups = {"group1", "group2"}, onUniqueConflicts = {ConflictAction.FAIL, ConflictAction.IGNORE})
* public String member2;
*
* @Column(name = "member3", uniqueGroups = {"group2"}, onUniqueConflicts = {ConflictAction.IGNORE})
* public String member3;
* }
*
* CREATE TABLE table_name (..., UNIQUE (member1, member2) ON CONFLICT FAIL, UNIQUE (member2, member3) ON CONFLICT IGNORE)
*/
public String[] uniqueGroups() default {};
public ConflictAction[] onUniqueConflicts() default {};
/*
* If set index = true, we will create a index with single column.
*
* Example:
*
* @Table(name = "table_name")
* public class Table extends Model {
* @Column(name = "member", index = true)
* public String member;
* }
*
* Execute CREATE INDEX index_table_name_member on table_name(member)
*/
public boolean index() default false;
/*
* If set indexGroups = {"group_name"}, we will create a index with group.
*
* Example:
*
* @Table(name = "table_name")
* public class Table extends Model {
* @Column(name = "member1", indexGroups = {"group1"})
* public String member1;
*
* @Column(name = "member2", indexGroups = {"group1", "group2"})
* public String member2;
*
* @Column(name = "member3", indexGroups = {"group2"})
* public String member3;
* }
*
* Execute CREATE INDEX index_table_name_group1 on table_name(member1, member2)
* Execute CREATE INDEX index_table_name_group2 on table_name(member2, member3)
*/
public String[] indexGroups() default {};
}

View File

@@ -0,0 +1,31 @@
package com.activeandroid.annotation;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
public static final String DEFAULT_ID_NAME = "Id";
public String name();
public String id() default DEFAULT_ID_NAME;
}

View File

@@ -0,0 +1,33 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import com.activeandroid.Model;
public final class Delete implements Sqlable {
public Delete() {
}
public From from(Class<? extends Model> table) {
return new From(table, this);
}
@Override
public String toSql() {
return "DELETE ";
}
}

View File

@@ -0,0 +1,344 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import android.text.TextUtils;
import com.activeandroid.Cache;
import com.activeandroid.Model;
import com.activeandroid.query.Join.JoinType;
import com.activeandroid.util.Log;
import com.activeandroid.util.SQLiteUtils;
import java.util.ArrayList;
import java.util.List;
public final class From implements Sqlable {
private Sqlable mQueryBase;
private Class<? extends Model> mType;
private String mAlias;
private List<Join> mJoins;
private final StringBuilder mWhere = new StringBuilder();
private String mGroupBy;
private String mHaving;
private String mOrderBy;
private String mLimit;
private String mOffset;
private List<Object> mArguments;
public From(Class<? extends Model> table, Sqlable queryBase) {
mType = table;
mJoins = new ArrayList<Join>();
mQueryBase = queryBase;
mJoins = new ArrayList<Join>();
mArguments = new ArrayList<Object>();
}
public From as(String alias) {
mAlias = alias;
return this;
}
public Join join(Class<? extends Model> table) {
Join join = new Join(this, table, null);
mJoins.add(join);
return join;
}
public Join leftJoin(Class<? extends Model> table) {
Join join = new Join(this, table, JoinType.LEFT);
mJoins.add(join);
return join;
}
public Join outerJoin(Class<? extends Model> table) {
Join join = new Join(this, table, JoinType.OUTER);
mJoins.add(join);
return join;
}
public Join innerJoin(Class<? extends Model> table) {
Join join = new Join(this, table, JoinType.INNER);
mJoins.add(join);
return join;
}
public Join crossJoin(Class<? extends Model> table) {
Join join = new Join(this, table, JoinType.CROSS);
mJoins.add(join);
return join;
}
public From where(String clause) {
// Chain conditions if a previous condition exists.
if (mWhere.length() > 0) {
mWhere.append(" AND ");
}
mWhere.append(clause);
return this;
}
public From where(String clause, Object... args) {
where(clause).addArguments(args);
return this;
}
public From and(String clause) {
return where(clause);
}
public From and(String clause, Object... args) {
return where(clause, args);
}
public From or(String clause) {
if (mWhere.length() > 0) {
mWhere.append(" OR ");
}
mWhere.append(clause);
return this;
}
public From or(String clause, Object... args) {
or(clause).addArguments(args);
return this;
}
public From groupBy(String groupBy) {
mGroupBy = groupBy;
return this;
}
public From having(String having) {
mHaving = having;
return this;
}
public From orderBy(String orderBy) {
mOrderBy = orderBy;
return this;
}
public From limit(int limit) {
return limit(String.valueOf(limit));
}
public From limit(String limit) {
mLimit = limit;
return this;
}
public From offset(int offset) {
return offset(String.valueOf(offset));
}
public From offset(String offset) {
mOffset = offset;
return this;
}
void addArguments(Object[] args) {
for(Object arg : args) {
if (arg.getClass() == boolean.class || arg.getClass() == Boolean.class) {
arg = (arg.equals(true) ? 1 : 0);
}
mArguments.add(arg);
}
}
private void addFrom(final StringBuilder sql) {
sql.append("FROM ");
sql.append(Cache.getTableName(mType)).append(" ");
if (mAlias != null) {
sql.append("AS ");
sql.append(mAlias);
sql.append(" ");
}
}
private void addJoins(final StringBuilder sql) {
for (final Join join : mJoins) {
sql.append(join.toSql());
}
}
private void addWhere(final StringBuilder sql) {
if (mWhere.length() > 0) {
sql.append("WHERE ");
sql.append(mWhere);
sql.append(" ");
}
}
private void addGroupBy(final StringBuilder sql) {
if (mGroupBy != null) {
sql.append("GROUP BY ");
sql.append(mGroupBy);
sql.append(" ");
}
}
private void addHaving(final StringBuilder sql) {
if (mHaving != null) {
sql.append("HAVING ");
sql.append(mHaving);
sql.append(" ");
}
}
private void addOrderBy(final StringBuilder sql) {
if (mOrderBy != null) {
sql.append("ORDER BY ");
sql.append(mOrderBy);
sql.append(" ");
}
}
private void addLimit(final StringBuilder sql) {
if (mLimit != null) {
sql.append("LIMIT ");
sql.append(mLimit);
sql.append(" ");
}
}
private void addOffset(final StringBuilder sql) {
if (mOffset != null) {
sql.append("OFFSET ");
sql.append(mOffset);
sql.append(" ");
}
}
private String sqlString(final StringBuilder sql) {
final String sqlString = sql.toString().trim();
// Don't waste time building the string
// unless we're going to log it.
if (Log.isEnabled()) {
Log.v(sqlString + " " + TextUtils.join(",", getArguments()));
}
return sqlString;
}
@Override
public String toSql() {
final StringBuilder sql = new StringBuilder();
sql.append(mQueryBase.toSql());
addFrom(sql);
addJoins(sql);
addWhere(sql);
addGroupBy(sql);
addHaving(sql);
addOrderBy(sql);
addLimit(sql);
addOffset(sql);
return sqlString(sql);
}
public String toExistsSql() {
final StringBuilder sql = new StringBuilder();
sql.append("SELECT EXISTS(SELECT 1 ");
addFrom(sql);
addJoins(sql);
addWhere(sql);
addGroupBy(sql);
addHaving(sql);
addLimit(sql);
addOffset(sql);
sql.append(")");
return sqlString(sql);
}
public String toCountSql() {
final StringBuilder sql = new StringBuilder();
sql.append("SELECT COUNT(*) ");
addFrom(sql);
addJoins(sql);
addWhere(sql);
addGroupBy(sql);
addHaving(sql);
addLimit(sql);
addOffset(sql);
return sqlString(sql);
}
public <T extends Model> List<T> execute() {
if (mQueryBase instanceof Select) {
return SQLiteUtils.rawQuery(mType, toSql(), getArguments());
} else {
SQLiteUtils.execSql(toSql(), getArguments());
return null;
}
}
public <T extends Model> T executeSingle() {
if (mQueryBase instanceof Select) {
limit(1);
return (T) SQLiteUtils.rawQuerySingle(mType, toSql(), getArguments());
} else {
limit(1);
SQLiteUtils.rawQuerySingle(mType, toSql(), getArguments()).delete();
return null;
}
}
/**
* Gets a value indicating whether the query returns any rows.
* @return <code>true</code> if the query returns at least one row; otherwise, <code>false</code>.
*/
public boolean exists() {
return SQLiteUtils.intQuery(toExistsSql(), getArguments()) != 0;
}
/**
* Gets the number of rows returned by the query.
*/
public int count() {
return SQLiteUtils.intQuery(toCountSql(), getArguments());
}
public String[] getArguments() {
final int size = mArguments.size();
final String[] args = new String[size];
for (int i = 0; i < size; i++) {
args[i] = mArguments.get(i).toString();
}
return args;
}
}

View File

@@ -0,0 +1,94 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import android.text.TextUtils;
import com.activeandroid.Cache;
import com.activeandroid.Model;
public final class Join implements Sqlable {
static enum JoinType {
LEFT, OUTER, INNER, CROSS
}
private From mFrom;
private Class<? extends Model> mType;
private String mAlias;
private JoinType mJoinType;
private String mOn;
private String[] mUsing;
Join(From from, Class<? extends Model> table, JoinType joinType) {
mFrom = from;
mType = table;
mJoinType = joinType;
}
public Join as(String alias) {
mAlias = alias;
return this;
}
public From on(String on) {
mOn = on;
return mFrom;
}
public From on(String on, Object... args) {
mOn = on;
mFrom.addArguments(args);
return mFrom;
}
public From using(String... columns) {
mUsing = columns;
return mFrom;
}
@Override
public String toSql() {
StringBuilder sql = new StringBuilder();
if (mJoinType != null) {
sql.append(mJoinType.toString()).append(" ");
}
sql.append("JOIN ");
sql.append(Cache.getTableName(mType));
sql.append(" ");
if (mAlias != null) {
sql.append("AS ");
sql.append(mAlias);
sql.append(" ");
}
if (mOn != null) {
sql.append("ON ");
sql.append(mOn);
sql.append(" ");
}
else if (mUsing != null) {
sql.append("USING (");
sql.append(TextUtils.join(", ", mUsing));
sql.append(") ");
}
return sql.toString();
}
}

View File

@@ -0,0 +1,93 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import android.text.TextUtils;
import com.activeandroid.Model;
public final class Select implements Sqlable {
private String[] mColumns;
private boolean mDistinct = false;
private boolean mAll = false;
public Select() {
}
public Select(String... columns) {
mColumns = columns;
}
public Select(Column... columns) {
final int size = columns.length;
mColumns = new String[size];
for (int i = 0; i < size; i++) {
mColumns[i] = columns[i].name + " AS " + columns[i].alias;
}
}
public Select distinct() {
mDistinct = true;
mAll = false;
return this;
}
public Select all() {
mDistinct = false;
mAll = true;
return this;
}
public From from(Class<? extends Model> table) {
return new From(table, this);
}
public static class Column {
String name;
String alias;
public Column(String name, String alias) {
this.name = name;
this.alias = alias;
}
}
@Override
public String toSql() {
StringBuilder sql = new StringBuilder();
sql.append("SELECT ");
if (mDistinct) {
sql.append("DISTINCT ");
}
else if (mAll) {
sql.append("ALL ");
}
if (mColumns != null && mColumns.length > 0) {
sql.append(TextUtils.join(", ", mColumns) + " ");
}
else {
sql.append("* ");
}
return sql.toString();
}
}

View File

@@ -0,0 +1,103 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import com.activeandroid.util.SQLiteUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public final class Set implements Sqlable {
private Update mUpdate;
private String mSet;
private String mWhere;
private List<Object> mSetArguments;
private List<Object> mWhereArguments;
public Set(Update queryBase, String set) {
mUpdate = queryBase;
mSet = set;
mSetArguments = new ArrayList<Object>();
mWhereArguments = new ArrayList<Object>();
}
public Set(Update queryBase, String set, Object... args) {
mUpdate = queryBase;
mSet = set;
mSetArguments = new ArrayList<Object>();
mWhereArguments = new ArrayList<Object>();
mSetArguments.addAll(Arrays.asList(args));
}
public Set where(String where) {
mWhere = where;
mWhereArguments.clear();
return this;
}
public Set where(String where, Object... args) {
mWhere = where;
mWhereArguments.clear();
mWhereArguments.addAll(Arrays.asList(args));
return this;
}
@Override
public String toSql() {
StringBuilder sql = new StringBuilder();
sql.append(mUpdate.toSql());
sql.append("SET ");
sql.append(mSet);
sql.append(" ");
if (mWhere != null) {
sql.append("WHERE ");
sql.append(mWhere);
sql.append(" ");
}
return sql.toString();
}
public void execute() {
SQLiteUtils.execSql(toSql(), getArguments());
}
public String[] getArguments() {
final int setSize = mSetArguments.size();
final int whereSize = mWhereArguments.size();
final String[] args = new String[setSize + whereSize];
for (int i = 0; i < setSize; i++) {
args[i] = mSetArguments.get(i).toString();
}
for (int i = 0; i < whereSize; i++) {
args[i + setSize] = mWhereArguments.get(i).toString();
}
return args;
}
}

View File

@@ -0,0 +1,21 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
public interface Sqlable {
public String toSql();
}

View File

@@ -0,0 +1,50 @@
package com.activeandroid.query;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import com.activeandroid.Cache;
import com.activeandroid.Model;
public final class Update implements Sqlable {
private Class<? extends Model> mType;
public Update(Class<? extends Model> table) {
mType = table;
}
public Set set(String set) {
return new Set(this, set);
}
public Set set(String set, Object... args) {
return new Set(this, set, args);
}
Class<? extends Model> getType() {
return mType;
}
@Override
public String toSql() {
StringBuilder sql = new StringBuilder();
sql.append("UPDATE ");
sql.append(Cache.getTableName(mType));
sql.append(" ");
return sql.toString();
}
}

View File

@@ -0,0 +1,29 @@
package com.activeandroid.serializer;
import java.math.BigDecimal;
public final class BigDecimalSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return BigDecimal.class;
}
public Class<?> getSerializedType() {
return String.class;
}
public String serialize(Object data) {
if (data == null) {
return null;
}
return ((BigDecimal) data).toString();
}
public BigDecimal deserialize(Object data) {
if (data == null) {
return null;
}
return new BigDecimal((String) data);
}
}

View File

@@ -0,0 +1,40 @@
package com.activeandroid.serializer;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.util.Calendar;
public final class CalendarSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return Calendar.class;
}
public Class<?> getSerializedType() {
return long.class;
}
public Long serialize(Object data) {
return ((Calendar) data).getTimeInMillis();
}
public Calendar deserialize(Object data) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis((Long) data);
return calendar;
}
}

View File

@@ -0,0 +1,46 @@
package com.activeandroid.serializer;
import java.io.File;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
public final class FileSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return File.class;
}
public Class<?> getSerializedType() {
return String.class;
}
public String serialize(Object data) {
if (data == null) {
return null;
}
return ((File) data).toString();
}
public File deserialize(Object data) {
if (data == null) {
return null;
}
return new File((String) data);
}
}

View File

@@ -0,0 +1,45 @@
package com.activeandroid.serializer;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.sql.Date;
public final class SqlDateSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return Date.class;
}
public Class<?> getSerializedType() {
return long.class;
}
public Long serialize(Object data) {
if (data == null) {
return null;
}
return ((Date) data).getTime();
}
public Date deserialize(Object data) {
if (data == null) {
return null;
}
return new Date((Long) data);
}
}

View File

@@ -0,0 +1,27 @@
package com.activeandroid.serializer;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
public abstract class TypeSerializer {
public abstract Class<?> getDeserializedType();
public abstract Class<?> getSerializedType();
public abstract Object serialize(Object data);
public abstract Object deserialize(Object data);
}

View File

@@ -0,0 +1,29 @@
package com.activeandroid.serializer;
import java.util.UUID;
public final class UUIDSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return UUID.class;
}
public Class<?> getSerializedType() {
return String.class;
}
public String serialize(Object data) {
if (data == null) {
return null;
}
return ((UUID) data).toString();
}
public UUID deserialize(Object data) {
if (data == null) {
return null;
}
return UUID.fromString((String)data);
}
}

View File

@@ -0,0 +1,45 @@
package com.activeandroid.serializer;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.util.Date;
public final class UtilDateSerializer extends TypeSerializer {
public Class<?> getDeserializedType() {
return Date.class;
}
public Class<?> getSerializedType() {
return long.class;
}
public Long serialize(Object data) {
if (data == null) {
return null;
}
return ((Date) data).getTime();
}
public Date deserialize(Object data) {
if (data == null) {
return null;
}
return new Date((Long) data);
}
}

View File

@@ -0,0 +1,71 @@
package com.activeandroid.util;
/*
* Copyright (C) 2014 Markus Pfeiffer
*
* 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.
*/
import android.database.Cursor;
import java.io.Closeable;
import java.io.IOException;
import com.activeandroid.util.Log;
public class IOUtils {
/**
* <p>
* Unconditionally close a {@link Closeable}.
* </p>
* Equivalent to {@link Closeable#close()}, except any exceptions will be ignored. This is
* typically used in finally blocks.
* @param closeable A {@link Closeable} to close.
*/
public static void closeQuietly(final Closeable closeable) {
if (closeable == null) {
return;
}
try {
closeable.close();
} catch (final IOException e) {
Log.e("Couldn't close closeable.", e);
}
}
/**
* <p>
* Unconditionally close a {@link Cursor}.
* </p>
* Equivalent to {@link Cursor#close()}, except any exceptions will be ignored. This is
* typically used in finally blocks.
* @param cursor A {@link Cursor} to close.
*/
public static void closeQuietly(final Cursor cursor) {
if (cursor == null) {
return;
}
try {
cursor.close();
} catch (final Exception e) {
Log.e("Couldn't close cursor.", e);
}
}
}

View File

@@ -0,0 +1,196 @@
package com.activeandroid.util;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
public final class Log {
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private static String sTag = "ActiveAndroid";
private static boolean sEnabled = false;
//////////////////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////
private Log() {
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static boolean isEnabled() {
return sEnabled;
}
public static void setEnabled(boolean enabled) {
sEnabled = enabled;
}
public static boolean isLoggingEnabled() {
return sEnabled;
}
public static int v(String msg) {
if (sEnabled) {
return android.util.Log.v(sTag, msg);
}
return 0;
}
public static int v(String tag, String msg) {
if (sEnabled) {
return android.util.Log.v(tag, msg);
}
return 0;
}
public static int v(String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.v(sTag, msg, tr);
}
return 0;
}
public static int v(String tag, String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.v(tag, msg, tr);
}
return 0;
}
public static int d(String msg) {
if (sEnabled) {
return android.util.Log.d(sTag, msg);
}
return 0;
}
public static int d(String tag, String msg) {
if (sEnabled) {
return android.util.Log.d(tag, msg);
}
return 0;
}
public static int d(String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.d(sTag, msg, tr);
}
return 0;
}
public static int d(String tag, String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.d(tag, msg, tr);
}
return 0;
}
public static int i(String msg) {
if (sEnabled) {
return android.util.Log.i(sTag, msg);
}
return 0;
}
public static int i(String tag, String msg) {
if (sEnabled) {
return android.util.Log.i(tag, msg);
}
return 0;
}
public static int i(String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.i(sTag, msg, tr);
}
return 0;
}
public static int i(String tag, String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.i(tag, msg, tr);
}
return 0;
}
public static int w(String msg) {
if (sEnabled) {
return android.util.Log.w(sTag, msg);
}
return 0;
}
public static int w(String tag, String msg) {
if (sEnabled) {
return android.util.Log.w(tag, msg);
}
return 0;
}
public static int w(String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.w(sTag, msg, tr);
}
return 0;
}
public static int w(String tag, String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.w(tag, msg, tr);
}
return 0;
}
public static int e(String msg) {
if (sEnabled) {
return android.util.Log.e(sTag, msg);
}
return 0;
}
public static int e(String tag, String msg) {
if (sEnabled) {
return android.util.Log.e(tag, msg);
}
return 0;
}
public static int e(String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.e(sTag, msg, tr);
}
return 0;
}
public static int e(String tag, String msg, Throwable tr) {
if (sEnabled) {
return android.util.Log.e(tag, msg, tr);
}
return 0;
}
public static int t(String msg, Object... args) {
if (sEnabled) {
return android.util.Log.v("test", String.format(msg, args));
}
return 0;
}
}

View File

@@ -0,0 +1,141 @@
package com.activeandroid.util;
/*
NaturalOrderComparator.java -- Perform 'natural order' comparisons of strings in Java.
Copyright (C) 2003 by Pierre-Luc Paour <natorder@paour.com>
Based on the C version by Martin Pool, of which this is more or less a straight conversion.
Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
import java.util.Comparator;
public class NaturalOrderComparator implements Comparator<Object> {
int compareRight(String a, String b) {
int bias = 0;
int ia = 0;
int ib = 0;
// The longest run of digits wins. That aside, the greatest
// value wins, but we can't know that it will until we've scanned
// both numbers to know that they have the same magnitude, so we
// remember it in BIAS.
for (;; ia++, ib++) {
char ca = charAt(a, ia);
char cb = charAt(b, ib);
if (!Character.isDigit(ca) && !Character.isDigit(cb)) {
return bias;
}
else if (!Character.isDigit(ca)) {
return -1;
}
else if (!Character.isDigit(cb)) {
return +1;
}
else if (ca < cb) {
if (bias == 0) {
bias = -1;
}
}
else if (ca > cb) {
if (bias == 0)
bias = +1;
}
else if (ca == 0 && cb == 0) {
return bias;
}
}
}
public int compare(Object o1, Object o2) {
String a = o1.toString();
String b = o2.toString();
int ia = 0, ib = 0;
int nza = 0, nzb = 0;
char ca, cb;
int result;
while (true) {
// only count the number of zeroes leading the last number compared
nza = nzb = 0;
ca = charAt(a, ia);
cb = charAt(b, ib);
// skip over leading spaces or zeros
while (Character.isSpaceChar(ca) || ca == '0') {
if (ca == '0') {
nza++;
}
else {
// only count consecutive zeroes
nza = 0;
}
ca = charAt(a, ++ia);
}
while (Character.isSpaceChar(cb) || cb == '0') {
if (cb == '0') {
nzb++;
}
else {
// only count consecutive zeroes
nzb = 0;
}
cb = charAt(b, ++ib);
}
// process run of digits
if (Character.isDigit(ca) && Character.isDigit(cb)) {
if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) {
return result;
}
}
if (ca == 0 && cb == 0) {
// The strings compare the same. Perhaps the caller
// will want to call strcmp to break the tie.
return nza - nzb;
}
if (ca < cb) {
return -1;
}
else if (ca > cb) {
return +1;
}
++ia;
++ib;
}
}
static char charAt(String s, int i) {
if (i >= s.length()) {
return 0;
}
else {
return s.charAt(i);
}
}
}

View File

@@ -0,0 +1,110 @@
package com.activeandroid.util;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.activeandroid.Model;
import com.activeandroid.annotation.Column;
import com.activeandroid.serializer.TypeSerializer;
public final class ReflectionUtils {
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static boolean isModel(Class<?> type) {
return isSubclassOf(type, Model.class) && (!Modifier.isAbstract(type.getModifiers()));
}
public static boolean isTypeSerializer(Class<?> type) {
return isSubclassOf(type, TypeSerializer.class);
}
// Meta-data
@SuppressWarnings("unchecked")
public static <T> T getMetaData(Context context, String name) {
try {
final ApplicationInfo ai = context.getPackageManager().getApplicationInfo(context.getPackageName(),
PackageManager.GET_META_DATA);
if (ai.metaData != null) {
return (T) ai.metaData.get(name);
}
}
catch (Exception e) {
Log.w("Couldn't find meta-data: " + name);
}
return null;
}
public static Set<Field> getDeclaredColumnFields(Class<?> type) {
Set<Field> declaredColumnFields = Collections.emptySet();
if (ReflectionUtils.isSubclassOf(type, Model.class) || Model.class.equals(type)) {
declaredColumnFields = new LinkedHashSet<Field>();
Field[] fields = type.getDeclaredFields();
Arrays.sort(fields, new Comparator<Field>() {
@Override
public int compare(Field field1, Field field2) {
return field2.getName().compareTo(field1.getName());
}
});
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
declaredColumnFields.add(field);
}
}
Class<?> parentType = type.getSuperclass();
if (parentType != null) {
declaredColumnFields.addAll(getDeclaredColumnFields(parentType));
}
}
return declaredColumnFields;
}
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static boolean isSubclassOf(Class<?> type, Class<?> superClass) {
if (type.getSuperclass() != null) {
if (type.getSuperclass().equals(superClass)) {
return true;
}
return isSubclassOf(type.getSuperclass(), superClass);
}
return false;
}
}

View File

@@ -0,0 +1,406 @@
package com.activeandroid.util;
/*
* Copyright (C) 2010 Michael Pardo
*
* 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.
*/
import android.database.Cursor;
import android.os.Build;
import android.text.TextUtils;
import com.activeandroid.Cache;
import com.activeandroid.Model;
import com.activeandroid.TableInfo;
import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Column.ConflictAction;
import com.activeandroid.serializer.TypeSerializer;
import java.lang.Long;
import java.lang.String;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public final class SQLiteUtils {
//////////////////////////////////////////////////////////////////////////////////////
// ENUMERATIONS
//////////////////////////////////////////////////////////////////////////////////////
public enum SQLiteType {
INTEGER, REAL, TEXT, BLOB
}
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC CONSTANTS
//////////////////////////////////////////////////////////////////////////////////////
public static final boolean FOREIGN_KEYS_SUPPORTED = Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE CONTSANTS
//////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("serial")
private static final HashMap<Class<?>, SQLiteType> TYPE_MAP = new HashMap<Class<?>, SQLiteType>() {
{
put(byte.class, SQLiteType.INTEGER);
put(short.class, SQLiteType.INTEGER);
put(int.class, SQLiteType.INTEGER);
put(long.class, SQLiteType.INTEGER);
put(float.class, SQLiteType.REAL);
put(double.class, SQLiteType.REAL);
put(boolean.class, SQLiteType.INTEGER);
put(char.class, SQLiteType.TEXT);
put(byte[].class, SQLiteType.BLOB);
put(Byte.class, SQLiteType.INTEGER);
put(Short.class, SQLiteType.INTEGER);
put(Integer.class, SQLiteType.INTEGER);
put(Long.class, SQLiteType.INTEGER);
put(Float.class, SQLiteType.REAL);
put(Double.class, SQLiteType.REAL);
put(Boolean.class, SQLiteType.INTEGER);
put(Character.class, SQLiteType.TEXT);
put(String.class, SQLiteType.TEXT);
put(Byte[].class, SQLiteType.BLOB);
}
};
//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE MEMBERS
//////////////////////////////////////////////////////////////////////////////////////
private static HashMap<String, List<String>> sIndexGroupMap;
private static HashMap<String, List<String>> sUniqueGroupMap;
private static HashMap<String, ConflictAction> sOnUniqueConflictsMap;
//////////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////////////////
public static void execSql(String sql) {
Cache.openDatabase().execSQL(sql);
}
public static void execSql(String sql, Object[] bindArgs) {
Cache.openDatabase().execSQL(sql, bindArgs);
}
public static <T extends Model> List<T> rawQuery(Class<? extends Model> type, String sql, String[] selectionArgs) {
Cursor cursor = Cache.openDatabase().rawQuery(sql, selectionArgs);
List<T> entities = processCursor(type, cursor);
cursor.close();
return entities;
}
public static int intQuery(final String sql, final String[] selectionArgs) {
final Cursor cursor = Cache.openDatabase().rawQuery(sql, selectionArgs);
final int number = processIntCursor(cursor);
cursor.close();
return number;
}
public static <T extends Model> T rawQuerySingle(Class<? extends Model> type, String sql, String[] selectionArgs) {
List<T> entities = rawQuery(type, sql, selectionArgs);
if (entities.size() > 0) {
return entities.get(0);
}
return null;
}
// Database creation
public static ArrayList<String> createUniqueDefinition(TableInfo tableInfo) {
final ArrayList<String> definitions = new ArrayList<String>();
sUniqueGroupMap = new HashMap<String, List<String>>();
sOnUniqueConflictsMap = new HashMap<String, ConflictAction>();
for (Field field : tableInfo.getFields()) {
createUniqueColumnDefinition(tableInfo, field);
}
if (sUniqueGroupMap.isEmpty()) {
return definitions;
}
Set<String> keySet = sUniqueGroupMap.keySet();
for (String key : keySet) {
List<String> group = sUniqueGroupMap.get(key);
ConflictAction conflictAction = sOnUniqueConflictsMap.get(key);
definitions.add(String.format("UNIQUE (%s) ON CONFLICT %s",
TextUtils.join(", ", group), conflictAction.toString()));
}
return definitions;
}
public static void createUniqueColumnDefinition(TableInfo tableInfo, Field field) {
final String name = tableInfo.getColumnName(field);
final Column column = field.getAnnotation(Column.class);
if (field.getName().equals("mId")) {
return;
}
String[] groups = column.uniqueGroups();
ConflictAction[] conflictActions = column.onUniqueConflicts();
if (groups.length != conflictActions.length)
return;
for (int i = 0; i < groups.length; i++) {
String group = groups[i];
ConflictAction conflictAction = conflictActions[i];
if (TextUtils.isEmpty(group))
continue;
List<String> list = sUniqueGroupMap.get(group);
if (list == null) {
list = new ArrayList<String>();
}
list.add(name);
sUniqueGroupMap.put(group, list);
sOnUniqueConflictsMap.put(group, conflictAction);
}
}
public static String[] createIndexDefinition(TableInfo tableInfo) {
final ArrayList<String> definitions = new ArrayList<String>();
sIndexGroupMap = new HashMap<String, List<String>>();
for (Field field : tableInfo.getFields()) {
createIndexColumnDefinition(tableInfo, field);
}
if (sIndexGroupMap.isEmpty()) {
return new String[0];
}
for (Map.Entry<String, List<String>> entry : sIndexGroupMap.entrySet()) {
definitions.add(String.format("CREATE INDEX IF NOT EXISTS %s on %s(%s);",
"index_" + tableInfo.getTableName() + "_" + entry.getKey(),
tableInfo.getTableName(), TextUtils.join(", ", entry.getValue())));
}
return definitions.toArray(new String[definitions.size()]);
}
public static void createIndexColumnDefinition(TableInfo tableInfo, Field field) {
final String name = tableInfo.getColumnName(field);
final Column column = field.getAnnotation(Column.class);
if (field.getName().equals("mId")) {
return;
}
if (column.index()) {
List<String> list = new ArrayList<String>();
list.add(name);
sIndexGroupMap.put(name, list);
}
String[] groups = column.indexGroups();
for (String group : groups) {
if (TextUtils.isEmpty(group))
continue;
List<String> list = sIndexGroupMap.get(group);
if (list == null) {
list = new ArrayList<String>();
}
list.add(name);
sIndexGroupMap.put(group, list);
}
}
public static String createTableDefinition(TableInfo tableInfo) {
final ArrayList<String> definitions = new ArrayList<String>();
for (Field field : tableInfo.getFields()) {
String definition = createColumnDefinition(tableInfo, field);
if (!TextUtils.isEmpty(definition)) {
definitions.add(definition);
}
}
definitions.addAll(createUniqueDefinition(tableInfo));
return String.format("CREATE TABLE IF NOT EXISTS %s (%s);", tableInfo.getTableName(),
TextUtils.join(", ", definitions));
}
@SuppressWarnings("unchecked")
public static String createColumnDefinition(TableInfo tableInfo, Field field) {
StringBuilder definition = new StringBuilder();
Class<?> type = field.getType();
final String name = tableInfo.getColumnName(field);
final TypeSerializer typeSerializer = Cache.getParserForType(field.getType());
final Column column = field.getAnnotation(Column.class);
if (typeSerializer != null) {
type = typeSerializer.getSerializedType();
}
if (TYPE_MAP.containsKey(type)) {
definition.append(name);
definition.append(" ");
definition.append(TYPE_MAP.get(type).toString());
}
else if (ReflectionUtils.isModel(type)) {
definition.append(name);
definition.append(" ");
definition.append(SQLiteType.INTEGER.toString());
}
else if (ReflectionUtils.isSubclassOf(type, Enum.class)) {
definition.append(name);
definition.append(" ");
definition.append(SQLiteType.TEXT.toString());
}
if (!TextUtils.isEmpty(definition)) {
if (name.equals(tableInfo.getIdName())) {
definition.append(" PRIMARY KEY AUTOINCREMENT");
}else if(column!=null){
if (column.length() > -1) {
definition.append("(");
definition.append(column.length());
definition.append(")");
}
if (column.notNull()) {
definition.append(" NOT NULL ON CONFLICT ");
definition.append(column.onNullConflict().toString());
}
if (column.unique()) {
definition.append(" UNIQUE ON CONFLICT ");
definition.append(column.onUniqueConflict().toString());
}
}
if (FOREIGN_KEYS_SUPPORTED && ReflectionUtils.isModel(type)) {
definition.append(" REFERENCES ");
definition.append(Cache.getTableInfo((Class<? extends Model>) type).getTableName());
definition.append("("+tableInfo.getIdName()+")");
definition.append(" ON DELETE ");
definition.append(column.onDelete().toString().replace("_", " "));
definition.append(" ON UPDATE ");
definition.append(column.onUpdate().toString().replace("_", " "));
}
}
else {
Log.e("No type mapping for: " + type.toString());
}
return definition.toString();
}
@SuppressWarnings("unchecked")
public static <T extends Model> List<T> processCursor(Class<? extends Model> type, Cursor cursor) {
TableInfo tableInfo = Cache.getTableInfo(type);
String idName = tableInfo.getIdName();
final List<T> entities = new ArrayList<T>();
try {
Constructor<?> entityConstructor = type.getConstructor();
if (cursor.moveToFirst()) {
/**
* Obtain the columns ordered to fix issue #106 (https://github.com/pardom/ActiveAndroid/issues/106)
* when the cursor have multiple columns with same name obtained from join tables.
*/
List<String> columnsOrdered = new ArrayList<String>(Arrays.asList(cursor.getColumnNames()));
do {
Model entity = Cache.getEntity(type, cursor.getLong(columnsOrdered.indexOf(idName)));
if (entity == null) {
entity = (T) entityConstructor.newInstance();
}
entity.loadFromCursor(cursor);
entities.add((T) entity);
}
while (cursor.moveToNext());
}
}
catch (NoSuchMethodException e) {
throw new RuntimeException(
"Your model " + type.getName() + " does not define a default " +
"constructor. The default constructor is required for " +
"now in ActiveAndroid models, as the process to " +
"populate the ORM model is : " +
"1. instantiate default model " +
"2. populate fields"
);
}
catch (Exception e) {
Log.e("Failed to process cursor.", e);
}
return entities;
}
private static int processIntCursor(final Cursor cursor) {
if (cursor.moveToFirst()) {
return cursor.getInt(0);
}
return 0;
}
public static List<String> lexSqlScript(String sqlScript) {
ArrayList<String> sl = new ArrayList<String>();
boolean inString = false, quoteNext = false;
StringBuilder b = new StringBuilder(100);
for (int i = 0; i < sqlScript.length(); i++) {
char c = sqlScript.charAt(i);
if (c == ';' && !inString && !quoteNext) {
sl.add(b.toString());
b = new StringBuilder(100);
inString = false;
quoteNext = false;
continue;
}
if (c == '\'' && !quoteNext) {
inString = !inString;
}
quoteNext = c == '\\' && !quoteNext;
b.append(c);
}
if (b.length() > 0) {
sl.add(b.toString());
}
return sl;
}
}

View File

@@ -0,0 +1,110 @@
package com.activeandroid.util;
/*
* Copyright (C) 2014 Markus Pfeiffer
*
* 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.
*/
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class SqlParser {
public final static int STATE_NONE = 0;
public final static int STATE_STRING = 1;
public final static int STATE_COMMENT = 2;
public final static int STATE_COMMENT_BLOCK = 3;
public static List<String> parse(final InputStream stream) throws IOException {
final BufferedInputStream buffer = new BufferedInputStream(stream);
final List<String> commands = new ArrayList<String>();
final StringBuffer sb = new StringBuffer();
try {
final Tokenizer tokenizer = new Tokenizer(buffer);
int state = STATE_NONE;
while (tokenizer.hasNext()) {
final char c = (char) tokenizer.next();
if (state == STATE_COMMENT_BLOCK) {
if (tokenizer.skip("*/")) {
state = STATE_NONE;
}
continue;
} else if (state == STATE_COMMENT) {
if (isNewLine(c)) {
state = STATE_NONE;
}
continue;
} else if (state == STATE_NONE && tokenizer.skip("/*")) {
state = STATE_COMMENT_BLOCK;
continue;
} else if (state == STATE_NONE && tokenizer.skip("--")) {
state = STATE_COMMENT;
continue;
} else if (state == STATE_NONE && c == ';') {
final String command = sb.toString().trim();
commands.add(command);
sb.setLength(0);
continue;
} else if (state == STATE_NONE && c == '\'') {
state = STATE_STRING;
} else if (state == STATE_STRING && c == '\'') {
state = STATE_NONE;
}
if (state == STATE_NONE || state == STATE_STRING) {
if (state == STATE_NONE && isWhitespace(c)) {
if (sb.length() > 0 && sb.charAt(sb.length() - 1) != ' ') {
sb.append(' ');
}
} else {
sb.append(c);
}
}
}
} finally {
IOUtils.closeQuietly(buffer);
}
if (sb.length() > 0) {
commands.add(sb.toString().trim());
}
return commands;
}
private static boolean isNewLine(final char c) {
return c == '\r' || c == '\n';
}
private static boolean isWhitespace(final char c) {
return c == '\r' || c == '\n' || c == '\t' || c == ' ';
}
}

View File

@@ -0,0 +1,76 @@
package com.activeandroid.util;
/*
* Copyright (C) 2014 Markus Pfeiffer
*
* 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.
*/
import java.io.IOException;
import java.io.InputStream;
public class Tokenizer {
private final InputStream mStream;
private boolean mIsNext;
private int mCurrent;
public Tokenizer(final InputStream in) {
this.mStream = in;
}
public boolean hasNext() throws IOException {
if (!this.mIsNext) {
this.mIsNext = true;
this.mCurrent = this.mStream.read();
}
return this.mCurrent != -1;
}
public int next() throws IOException {
if (!this.mIsNext) {
this.mCurrent = this.mStream.read();
}
this.mIsNext = false;
return this.mCurrent;
}
public boolean skip(final String s) throws IOException {
if (s == null || s.length() == 0) {
return false;
}
if (s.charAt(0) != this.mCurrent) {
return false;
}
final int len = s.length();
this.mStream.mark(len - 1);
for (int n = 1; n < len; n++) {
final int value = this.mStream.read();
if (value != s.charAt(n)) {
this.mStream.reset();
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,57 @@
package com.activeandroid.widget;
import java.util.Collection;
import java.util.List;
import android.content.Context;
import android.widget.ArrayAdapter;
import com.activeandroid.Model;
public class ModelAdapter<T extends Model> extends ArrayAdapter<T> {
public ModelAdapter(Context context, int textViewResourceId) {
super(context, textViewResourceId);
}
public ModelAdapter(Context context, int resource, int textViewResourceId) {
super(context, resource, textViewResourceId);
}
public ModelAdapter(Context context, int textViewResourceId, List<T> objects) {
super(context, textViewResourceId, objects);
}
public ModelAdapter(Context context, int resource, int textViewResourceId, List<T> objects) {
super(context, resource, textViewResourceId, objects);
}
/**
* Clears the adapter and, if data != null, fills if with new Items.
*
* @param collection A Collection&lt;? extends T&gt; which members get added to the adapter.
*/
public void setData(Collection<? extends T> collection) {
clear();
if (collection != null) {
for (T item : collection) {
add(item);
}
}
}
/**
* @return The Id of the record at position.
*/
@Override
public long getItemId(int position) {
T item = getItem(position);
if (item != null) {
return item.getId();
}
else {
return -1;
}
}
}

View File

@@ -256,7 +256,7 @@ public class FrequencyChart extends ScrollableChart
float scale = 1.0f/maxFreq * value;
float radius = maxRadius * scale;
int colorIndex = Math.round((colors.length-1) * scale);
int colorIndex = Math.min(colors.length - 1, Math.round((colors.length - 1) * scale));
pGraph.setColor(colors[colorIndex]);
canvas.drawCircle(rect.centerX(), rect.centerY(), radius, pGraph);
}

View File

@@ -32,7 +32,8 @@ import org.isoron.uhabits.utils.*;
import java.text.*;
import java.util.*;
import static org.isoron.uhabits.models.Checkmark.*;
import static org.isoron.uhabits.models.Checkmark.CHECKED_EXPLICITLY;
import static org.isoron.uhabits.models.Checkmark.UNCHECKED;
public class HistoryChart extends ScrollableChart
{
@@ -112,10 +113,21 @@ public class HistoryChart extends ScrollableChart
if (!isEditable) return false;
performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP);
float x, y;
try
{
int pointerId = e.getPointerId(0);
float x = e.getX(pointerId);
float y = e.getY(pointerId);
x = e.getX(pointerId);
y = e.getY(pointerId);
}
catch (RuntimeException ex)
{
// Android often throws IllegalArgumentException here. Apparently,
// the pointer id may become invalid shortly after calling
// e.getPointerId.
return false;
}
final Long timestamp = positionToTimestamp(x, y);
if (timestamp == null) return false;

View File

@@ -105,11 +105,10 @@ public class HabitCardListAdapter
* Returns the item that occupies a certain position on the list
*
* @param position position of the item
* @return the item at given position
* @throws IndexOutOfBoundsException if position is not valid
* @return the item at given position or null if position is invalid
*/
@Deprecated
@NonNull
@Nullable
public Habit getItem(int position)
{
return cache.getHabitByPosition(position);
@@ -314,6 +313,8 @@ public class HabitCardListAdapter
public void toggleSelection(int position)
{
Habit h = getItem(position);
if (h == null) return;
int k = selected.indexOf(h);
if (k < 0) selected.add(h);
else selected.remove(h);

View File

@@ -93,12 +93,12 @@ public class HabitCardListCache implements CommandRunner.Listener
* Returns the habits that occupies a certain position on the list.
*
* @param position the position of the habit
* @return the habit at given position
* @throws IndexOutOfBoundsException if position is not valid
* @return the habit at given position or null if position is invalid
*/
@NonNull
public Habit getHabitByPosition(int position)
@Nullable
public synchronized Habit getHabitByPosition(int position)
{
if(position < 0 || position >= data.habits.size()) return null;
return data.habits.get(position);
}

View File

@@ -143,12 +143,13 @@ public class HabitCardView extends FrameLayout
updateBackground(isSelected);
}
public void triggerRipple(long timestamp)
public synchronized void triggerRipple(long timestamp)
{
long today = DateUtils.getStartOfToday();
long day = DateUtils.millisecondsInOneDay;
int offset = (int) ((today - timestamp) / day) - dataOffset;
CheckmarkButtonView button = checkmarkPanel.indexToButton(offset);
if (button == null) return;
float y = button.getHeight() / 2.0f;
float x = checkmarkPanel.getX() + button.getX() + button.getWidth() / 2;

View File

@@ -22,12 +22,16 @@ package org.isoron.uhabits.activities.settings;
import android.app.backup.*;
import android.content.*;
import android.os.*;
import android.provider.*;
import android.support.v7.preference.*;
import org.isoron.uhabits.R;
import org.isoron.uhabits.activities.habits.list.*;
import org.isoron.uhabits.notifications.*;
import org.isoron.uhabits.utils.*;
import static android.os.Build.VERSION.*;
public class SettingsFragment extends PreferenceFragmentCompat
implements SharedPreferences.OnSharedPreferenceChangeListener
{
@@ -61,6 +65,14 @@ public class SettingsFragment extends PreferenceFragmentCompat
setResultOnPreferenceClick("bugReport", ListHabitsScreen.RESULT_BUG_REPORT);
updateRingtoneDescription();
if (SDK_INT < Build.VERSION_CODES.O)
findPreference("reminderCustomize").setVisible(false);
else
{
findPreference("reminderSound").setVisible(false);
findPreference("pref_snooze_interval").setVisible(false);
}
}
@Override
@@ -88,6 +100,17 @@ public class SettingsFragment extends PreferenceFragmentCompat
RINGTONE_REQUEST_CODE);
return true;
}
else if (key.equals("reminderCustomize"))
{
if (SDK_INT < Build.VERSION_CODES.O) return true;
NotificationTray.createAndroidNotificationChannel(getContext());
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getContext().getPackageName());
intent.putExtra(Settings.EXTRA_CHANNEL_ID, NotificationTray.REMINDERS_CHANNEL_ID);
startActivity(intent);
return true;
}
return super.onPreferenceTreeClick(preference);
}

View File

@@ -119,7 +119,7 @@ public class HabitsCSVExporter
{
String sane = sanitizeFilename(h.getName());
String habitDirName =
String.format("%03d %s", allHabits.indexOf(h) + 1, sane);
String.format(Locale.US, "%03d %s", allHabits.indexOf(h) + 1, sane);
habitDirName = habitDirName.trim() + "/";
new File(exportDirName + habitDirName).mkdirs();
@@ -202,7 +202,7 @@ public class HabitsCSVExporter
checksWriter.write(String.valueOf(checkmarks.get(j)[i]));
checksWriter.write(DELIMITER);
String score =
String.format("%.4f", ((float) scores.get(j)[i]) / Score.MAX_VALUE);
String.format(Locale.US, "%.4f", ((float) scores.get(j)[i]) / Score.MAX_VALUE);
scoresWriter.write(score);
scoresWriter.write(DELIMITER);
}

View File

@@ -81,7 +81,7 @@ public abstract class ScoreList implements Iterable<Score>
* @param timestamp the timestamp of a day
* @return score value for that day
*/
public final synchronized int getValue(long timestamp)
public synchronized final int getValue(long timestamp)
{
compute(timestamp, timestamp);
Score s = getComputedByTimestamp(timestamp);
@@ -173,7 +173,7 @@ public abstract class ScoreList implements Iterable<Score>
{
String timestamp = dateFormat.format(s.getTimestamp());
String score =
String.format("%.4f", ((float) s.getValue()) / Score.MAX_VALUE);
String.format(Locale.US, "%.4f", ((float) s.getValue()) / Score.MAX_VALUE);
out.write(String.format("%s,%s\n", timestamp, score));
}
}

View File

@@ -53,38 +53,27 @@ public class SQLiteCheckmarkList extends CheckmarkList
@Nullable
private CachedData cache;
@NonNull
private final SQLiteStatement invalidateStatement;
@NonNull
private final SQLiteStatement addStatement;
@NonNull
private final SQLiteDatabase db;
public SQLiteCheckmarkList(Habit habit)
{
super(habit);
sqlite = new SQLiteUtils<>(CheckmarkRecord.class);
db = Cache.openDatabase();
addStatement = db.compileStatement(ADD_QUERY);
invalidateStatement = db.compileStatement(INVALIDATE_QUERY);
}
@Override
public void add(List<Checkmark> checkmarks)
{
check(habit.getId());
SQLiteDatabase db = Cache.openDatabase();
SQLiteStatement statement = db.compileStatement(ADD_QUERY);
db.beginTransaction();
try
{
for (Checkmark c : checkmarks)
{
addStatement.bindLong(1, habit.getId());
addStatement.bindLong(2, c.getTimestamp());
addStatement.bindLong(3, c.getValue());
addStatement.execute();
statement.bindLong(1, habit.getId());
statement.bindLong(2, c.getTimestamp());
statement.bindLong(3, c.getValue());
statement.execute();
}
db.setTransactionSuccessful();
@@ -115,14 +104,7 @@ public class SQLiteCheckmarkList extends CheckmarkList
List<CheckmarkRecord> records = sqlite.query(query, params);
for (CheckmarkRecord record : records) record.habit = habitRecord;
int nDays = DateUtils.getDaysBetween(fromTimestamp, toTimestamp) + 1;
if (records.size() != nDays)
{
throw new InconsistentDatabaseException(
String.format("habit=%s, %d expected, %d found",
habit.getName(), nDays, records.size()));
}
records = fixRecords(records, habitRecord, fromTimestamp, toTimestamp);
return toCheckmarks(records);
}
@@ -139,9 +121,11 @@ public class SQLiteCheckmarkList extends CheckmarkList
public void invalidateNewerThan(long timestamp)
{
cache = null;
invalidateStatement.bindLong(1, habit.getId());
invalidateStatement.bindLong(2, timestamp);
invalidateStatement.execute();
SQLiteDatabase db = Cache.openDatabase();
SQLiteStatement statement = db.compileStatement(INVALIDATE_QUERY);
statement.bindLong(1, habit.getId());
statement.bindLong(2, timestamp);
statement.execute();
observable.notifyListeners();
}
@@ -198,6 +182,28 @@ public class SQLiteCheckmarkList extends CheckmarkList
return checkmarks;
}
public static List<CheckmarkRecord> fixRecords(List<CheckmarkRecord> original,
HabitRecord habit,
long fromTimestamp,
long toTimestamp)
{
long day = DateUtils.millisecondsInOneDay;
ArrayList<CheckmarkRecord> records = new ArrayList<>();
for (long t = toTimestamp; t >= fromTimestamp; t -= day)
records.add(new CheckmarkRecord(habit, t, Checkmark.UNCHECKED));
for (CheckmarkRecord record : original)
{
if ((toTimestamp - record.timestamp) % day != 0) continue;
int offset = (int) ((toTimestamp - record.timestamp) / day);
if (offset < 0 || offset >= records.size()) continue;
records.set(offset, record);
}
return records;
}
private static class CachedData
{
int todayValue;

View File

@@ -49,14 +49,6 @@ public class SQLiteScoreList extends ScoreList
@NonNull
private final SQLiteUtils<ScoreRecord> sqlite;
@NonNull
private final SQLiteStatement invalidateStatement;
@NonNull
private final SQLiteStatement addStatement;
private final SQLiteDatabase db;
@Nullable
private CachedData cache = null;
@@ -69,25 +61,23 @@ public class SQLiteScoreList extends ScoreList
{
super(habit);
sqlite = new SQLiteUtils<>(ScoreRecord.class);
db = Cache.openDatabase();
addStatement = db.compileStatement(ADD_QUERY);
invalidateStatement = db.compileStatement(INVALIDATE_QUERY);
}
@Override
public void add(List<Score> scores)
{
check(habit.getId());
SQLiteDatabase db = Cache.openDatabase();
SQLiteStatement statement = db.compileStatement(ADD_QUERY);
db.beginTransaction();
try
{
for (Score s : scores)
{
addStatement.bindLong(1, habit.getId());
addStatement.bindLong(2, s.getTimestamp());
addStatement.bindLong(3, s.getValue());
addStatement.execute();
statement.bindLong(1, habit.getId());
statement.bindLong(2, s.getTimestamp());
statement.bindLong(3, s.getValue());
statement.execute();
}
db.setTransactionSuccessful();
@@ -100,7 +90,8 @@ public class SQLiteScoreList extends ScoreList
@NonNull
@Override
public List<Score> getByInterval(long fromTimestamp, long toTimestamp)
public synchronized List<Score> getByInterval(long fromTimestamp,
long toTimestamp)
{
check(habit.getId());
compute(fromTimestamp, toTimestamp);
@@ -137,7 +128,7 @@ public class SQLiteScoreList extends ScoreList
}
@Override
public int getTodayValue()
public synchronized int getTodayValue()
{
if (cache == null || cache.expired())
cache = new CachedData(super.getTodayValue());
@@ -146,12 +137,14 @@ public class SQLiteScoreList extends ScoreList
}
@Override
public void invalidateNewerThan(long timestamp)
public synchronized void invalidateNewerThan(long timestamp)
{
cache = null;
invalidateStatement.bindLong(1, habit.getId());
invalidateStatement.bindLong(2, timestamp);
invalidateStatement.execute();
SQLiteDatabase db = Cache.openDatabase();
SQLiteStatement statement = db.compileStatement(INVALIDATE_QUERY);
statement.bindLong(1, habit.getId());
statement.bindLong(2, timestamp);
statement.execute();
getObservable().notifyListeners();
}

View File

@@ -37,22 +37,19 @@ import java.util.*;
*/
public class SQLiteStreakList extends StreakList
{
private static final String INVALIDATE_QUERY =
"delete from Streak where habit = ? and end >= ?";
private HabitRecord habitRecord;
@NonNull
private final SQLiteUtils<StreakRecord> sqlite;
private final SQLiteStatement invalidateStatement;
public SQLiteStreakList(Habit habit)
{
super(habit);
sqlite = new SQLiteUtils<>(StreakRecord.class);
SQLiteDatabase db = Cache.openDatabase();
String invalidateQuery = "delete from Streak where habit = ? " +
"and end >= ?";
invalidateStatement = db.compileStatement(invalidateQuery);
}
@Override
@@ -81,9 +78,11 @@ public class SQLiteStreakList extends StreakList
@Override
public void invalidateNewerThan(long timestamp)
{
invalidateStatement.bindLong(1, habit.getId());
invalidateStatement.bindLong(2, timestamp - DateUtils.millisecondsInOneDay);
invalidateStatement.execute();
SQLiteDatabase db = Cache.openDatabase();
SQLiteStatement statement = db.compileStatement(INVALIDATE_QUERY);
statement.bindLong(1, habit.getId());
statement.bindLong(2, timestamp - DateUtils.millisecondsInOneDay);
statement.execute();
observable.notifyListeners();
}

View File

@@ -24,6 +24,7 @@ import android.database.*;
import com.activeandroid.*;
import com.activeandroid.annotation.*;
import org.apache.commons.lang3.builder.*;
import org.isoron.uhabits.models.*;
/**
@@ -53,6 +54,17 @@ public class CheckmarkRecord extends Model implements SQLiteRecord
@Column(name = "value")
public Integer value;
public CheckmarkRecord()
{
}
public CheckmarkRecord(HabitRecord habit, Long timestamp, Integer value)
{
this.habit = habit;
this.timestamp = timestamp;
this.value = value;
}
@Override
public void copyFrom(Cursor c)
{
@@ -64,4 +76,40 @@ public class CheckmarkRecord extends Model implements SQLiteRecord
{
return new Checkmark(timestamp, value);
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CheckmarkRecord that = (CheckmarkRecord) o;
return new EqualsBuilder()
.append(habit, that.habit)
.append(timestamp, that.timestamp)
.append(value, that.value)
.isEquals();
}
@Override
public int hashCode()
{
return new HashCodeBuilder(17, 37)
.append(habit)
.append(timestamp)
.append(value)
.toHashCode();
}
@Override
public String toString()
{
return new ToStringBuilder(this)
.append("habit", habit)
.append("timestamp", timestamp)
.append("value", value)
.toString();
}
}

View File

@@ -22,6 +22,7 @@ package org.isoron.uhabits.notifications;
import android.app.*;
import android.content.*;
import android.graphics.*;
import android.os.*;
import android.support.annotation.*;
import android.support.v4.app.*;
import android.support.v4.app.NotificationCompat.*;
@@ -39,12 +40,15 @@ import java.util.*;
import javax.inject.*;
import static android.graphics.BitmapFactory.*;
import static android.os.Build.VERSION.*;
import static org.isoron.uhabits.utils.RingtoneUtils.*;
@AppScope
public class NotificationTray
implements CommandRunner.Listener, Preferences.Listener
{
public static final String REMINDERS_CHANNEL_ID = "REMINDERS";
@NonNull
private final Context context;
@@ -196,10 +200,6 @@ public class NotificationTray
context.getString(R.string.check),
pendingIntents.addCheckmark(habit, timestamp));
Action snoozeAction = new Action(R.drawable.ic_action_snooze,
context.getString(R.string.snooze),
pendingIntents.snoozeNotification(habit));
Bitmap wearableBg =
decodeResource(context.getResources(), R.drawable.stripe);
@@ -208,30 +208,44 @@ public class NotificationTray
// WearableExtender.
WearableExtender wearableExtender = new WearableExtender()
.setBackground(wearableBg)
.addAction(checkAction)
.addAction(snoozeAction);
.addAction(checkAction);
Notification notification = new NotificationCompat.Builder(context)
Builder builder = new Builder(context, REMINDERS_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(habit.getName())
.setContentText(habit.getDescription())
.setContentIntent(pendingIntents.showHabit(habit))
.setDeleteIntent(pendingIntents.dismissNotification(habit))
.addAction(checkAction)
.addAction(snoozeAction)
.setSound(getRingtoneUri(context))
.extend(wearableExtender)
.setWhen(reminderTime)
.setShowWhen(true)
.setOngoing(preferences.shouldMakeNotificationsSticky())
.build();
.setOngoing(preferences.shouldMakeNotificationsSticky());
if(SDK_INT < Build.VERSION_CODES.O) {
Action snoozeAction = new Action(R.drawable.ic_action_snooze,
context.getString(R.string.snooze),
pendingIntents.snoozeNotification(habit));
wearableExtender.addAction(snoozeAction);
builder.addAction(snoozeAction);
}
builder.extend(wearableExtender);
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(
Activity.NOTIFICATION_SERVICE);
createAndroidNotificationChannel(context);
int notificationId = getNotificationId(habit);
notificationManager.notify(notificationId, notification);
try {
notificationManager.notify(notificationId, builder.build());
} catch(RuntimeException e) {
builder.setSound(null);
notificationManager.notify(notificationId, builder.build());
}
}
private boolean shouldShowReminderToday()
@@ -245,4 +259,19 @@ public class NotificationTray
return reminderDays[weekday];
}
}
public static void createAndroidNotificationChannel(Context context) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(
Activity.NOTIFICATION_SERVICE);
if (SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel channel =
new NotificationChannel(REMINDERS_CHANNEL_ID,
context.getResources().getString(R.string.reminder),
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
}
}

View File

@@ -28,6 +28,8 @@ import org.isoron.uhabits.utils.*;
import javax.inject.*;
import static org.isoron.uhabits.utils.DateUtils.*;
@ReceiverScope
public class ReminderController
{
@@ -66,7 +68,7 @@ public class ReminderController
{
long snoozeInterval = preferences.getSnoozeInterval();
long now = DateUtils.getLocalTime();
long now = applyTimezone(getLocalTime());
long reminderTime = now + snoozeInterval * 60 * 1000;
reminderScheduler.schedule(habit, reminderTime);

View File

@@ -42,8 +42,7 @@ public abstract class DateUtils
public static long applyTimezone(long localTimestamp)
{
TimeZone tz = getTimezone();
long now = new Date(localTimestamp).getTime();
return now - tz.getOffset(now);
return localTimestamp - tz.getOffset(localTimestamp - tz.getOffset(localTimestamp));
}
public static String formatHeaderDate(GregorianCalendar day)
@@ -230,8 +229,7 @@ public abstract class DateUtils
public static long removeTimezone(long timestamp)
{
TimeZone tz = getTimezone();
long now = new Date(timestamp).getTime();
return now + tz.getOffset(now);
return timestamp + tz.getOffset(timestamp);
}
public static void setFixedLocalTime(Long timestamp)

View File

@@ -146,6 +146,14 @@
style="@style/About.Item"
android:text="Angga Rifandi (Bahasa Indonesia)"/>
<TextView
style="@style/About.Item"
android:text="raden20 (Bahasa Indonesia)"/>
<TextView
style="@style/About.Item"
android:text="azzamsa (Bahasa Indonesia)"/>
<TextView
style="@style/About.Item"
android:text="David Nos (Català)"/>
@@ -178,6 +186,10 @@
style="@style/About.Item"
android:text="Can Altas (Deutsch)"/>
<TextView
style="@style/About.Item"
android:text="Laura Sophie (Deutsch)"/>
<TextView
style="@style/About.Item"
android:text="Ander Raso Vazquez (Español)"/>
@@ -186,6 +198,10 @@
style="@style/About.Item"
android:text="Beriain (Euskara)"/>
<TextView
style="@style/About.Item"
android:text="Osoitz (Euskara)"/>
<TextView
style="@style/About.Item"
android:text="Andreas Michelakis (Ελληνικά)"/>
@@ -198,6 +214,10 @@
style="@style/About.Item"
android:text="Saeed Esmaili (Fārsi)"/>
<TextView
style="@style/About.Item"
android:text="Behnood HRazy (Fārsi)"/>
<TextView
style="@style/About.Item"
android:text="François Mahé (Français)"/>
@@ -214,6 +234,10 @@
style="@style/About.Item"
android:text="Michael Faille (Français)"/>
<TextView
style="@style/About.Item"
android:text="Tiralka (Français)"/>
<TextView
style="@style/About.Item"
android:text="Ivan Krušlin (Hrvatski)"/>
@@ -230,6 +254,10 @@
style="@style/About.Item"
android:text="Jelle den Butter (Nederlands)"/>
<TextView
style="@style/About.Item"
android:text="nitovf9292 (Norsk)"/>
<TextView
style="@style/About.Item"
android:text="Adam Jurkiewicz (Polski)"/>
@@ -250,18 +278,26 @@
style="@style/About.Item"
android:text="Andrei Pleș (Română)"/>
<TextView
style="@style/About.Item"
android:text="Andreea Muscalagiu (Română)"/>
<TextView
style="@style/About.Item"
android:text="Dušan Strgar (Slovenščina)"/>
<TextView
style="@style/About.Item"
android:text="Dalecarlian (Svenska)"/>
android:text="Alexander Jansson (Svenska)"/>
<TextView
style="@style/About.Item"
android:text="Robin (Svenska)"/>
<TextView
style="@style/About.Item"
android:text="Sofia Veijonen (Suomen kieli)"/>
<TextView
style="@style/About.Item"
android:text="Đorđe Vasiljević (српски)"/>
@@ -270,6 +306,10 @@
style="@style/About.Item"
android:text="Caner Başaran (Türkçe)"/>
<TextView
style="@style/About.Item"
android:text="hodanli (Türkçe)"/>
<TextView
style="@style/About.Item"
android:text="Yurii Stavytskyi (Українська)"/>
@@ -278,6 +318,14 @@
style="@style/About.Item"
android:text="Rystard (Українська)"/>
<TextView
style="@style/About.Item"
android:text="Oglaigh Rystard (Українська)"/>
<TextView
style="@style/About.Item"
android:text="taras-ko (Українська)"/>
<TextView
style="@style/About.Item"
android:text="Limin Lu (中文)"/>
@@ -310,10 +358,22 @@
style="@style/About.Item"
android:text="Al Alloush (العَرَبِية‎)"/>
<TextView
style="@style/About.Item"
android:text="Boula (العَرَبِية‎)"/>
<TextView
style="@style/About.Item"
android:text="Israa Z (العَرَبِية‎)"/>
<TextView
style="@style/About.Item"
android:text="Josh Graham (한국어 )"/>
<TextView
style="@style/About.Item"
android:text="Seoyul (한국어 )"/>
<TextView
style="@style/About.Item"
android:text="Aman Satnami (हिन्दी)"/>
@@ -326,6 +386,34 @@
style="@style/About.Item"
android:text="Yoav Argov (עברית‎)"/>
<TextView
style="@style/About.Item"
android:text="Mahdi Nasiri (فارسی‎)"/>
<TextView
style="@style/About.Item"
android:text="Mohammed Imthath (தமிழ்‎)"/>
<TextView
style="@style/About.Item"
android:text="magimai (தமிழ்‎)"/>
<TextView
style="@style/About.Item"
android:text="Anshoe (தமிழ்‎)"/>
<TextView
style="@style/About.Item"
android:text="Trần Thái (Tiếng Việt)"/>
<TextView
style="@style/About.Item"
android:text="Anh Quân (Tiếng Việt)"/>
<TextView
style="@style/About.Item"
android:text="pnhpnh (Tiếng Việt)"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -19,6 +19,34 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">Herhaalde Gewoonte Boekhouer</string>
<string name="main_activity_title">Gewoontes</string>
<string name="action_settings">Instellings</string>
<string name="edit">Redigeer</string>
<string name="delete">Verwyder</string>
<string name="archive">Argiveer</string>
<string name="unarchive">Deargiveer</string>
<string name="add_habit">Voeg gewoonte by</string>
<string name="color_picker_default_title">Verander kleur</string>
<string name="toast_habit_created">Gewoonte geskep</string>
<string name="toast_habit_deleted">Gewoontes verwyder</string>
<string name="toast_habit_restored">Gewoontes herstel</string>
<string name="toast_nothing_to_undo">Niks om terug te doen nie</string>
<string name="toast_nothing_to_redo">Niks om oor te doen nie</string>
<string name="toast_habit_changed">Gewoonte verander</string>
<string name="toast_habit_changed_back">Gewoonte terug verander</string>
<string name="toast_habit_archived">Gewoontes geargiveer</string>
<string name="toast_habit_unarchived">Gewoontes gedeargiveer</string>
<string name="overview">Oorsig</string>
<!-- App introduction -->
<string name="intro_title_1">Welkom</string>
<string name="interval_15_minutes">15 minute</string>
<string name="interval_30_minutes">30 minute</string>
<string name="interval_1_hour">1 uur</string>
<string name="interval_2_hour">2 ure</string>
<string name="interval_4_hour">4 ure</string>
<string name="interval_8_hour">8 ure</string>
<string name="interval_24_hour">24 ure</string>
<string name="settings">Instellings</string>
<!-- Middle part of the sentence '1 time in xx days' -->
</resources>

View File

@@ -19,63 +19,63 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">لوب ملاحق العادة </string>
<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="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_habit_restored">تم ترجيع عادة</string>
<string name="toast_nothing_to_undo">لا شيء للتراجع</string>
<string name="toast_nothing_to_redo">لا شيء لتكرار</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_habit_restored">تم إستعادة العادات</string>
<string name="toast_nothing_to_undo">لا شيء للألغاء</string>
<string name="toast_nothing_to_redo">لا شيء للإعادة</string>
<string name="toast_habit_changed">تم تغيير عادة</string>
<string name="toast_habit_changed_back">تم ترجيع العادة إلى أصلها</string>
<string name="toast_habit_archived">تم أرشيف العادات</string>
<string name="toast_habit_unarchived">تم إزالة العادة من الأرشيف </string>
<string name="toast_habit_changed_back">تم أرجاع العادة إلى أصلها</string>
<string name="toast_habit_archived">تم أرشفه العادات</string>
<string name="toast_habit_unarchived">تم الغاء ارشفه العادات</string>
<string name="overview">نظرة عامة</string>
<string name="habit_strength">قوة العادة</string>
<string name="history">التاريخ</string>
<string name="clear">مسح</string>
<string name="history">السجل</string>
<string name="clear">إزالة</string>
<string name="description_hint">السؤال (هل ... اليوم؟)</string>
<string name="repeat">كرر</string>
<string name="times_every">مرات في</string>
<string name="repeat">كرره</string>
<string name="times_every">مرات كل</string>
<string name="days">أيام</string>
<string name="reminder">تذكير</string>
<string name="discard">حذف</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="streaks">الانجازات</string>
<string name="no_habits_found">لا يوجد لديك عادات مفعلة</string>
<string name="long_press_to_toggle">أضغط و إستمر لتحقق أو ازل</string>
<string name="reminder_off">أوقف</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="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>
<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_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="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">ساعة واحدة</string>
<string name="interval_2_hour">ساعتين</string>
<string name="interval_4_hour">أربع ساعات</string>
<string name="interval_8_hour">ثماني ساعات</string>
<string name="interval_24_hour">24 ساعة</string>
<string name="pref_toggle_title">تبديل بكبسه</string>
<string name="interval_2_hour">ساعتان</string>
<string name="interval_4_hour">٤ ساعات</string>
<string name="interval_8_hour">8 ساعات</string>
<string name="interval_24_hour">٢٤ ساعة</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">تقييم هذا التطبيق على جوجل بلاي</string>
@@ -92,6 +92,7 @@
<string name="hint_landscape">يمكنك ان ترى المزيد أيام عن طريق وضع الهاتف في وضع أفقي.</string>
<string name="delete_habits">حذف عادات</string>
<string name="delete_habits_message">سيتم حذف عادات بشكل دائم. هذا العمل لا يمكن التراجع عنه.</string>
<string name="habit_not_found">العادة حذفت/لم يتم العثور عليها</string>
<string name="weekends">عطلة نهاية الأسبوع</string>
<string name="any_weekday">أيام الأسبوع</string>
<string name="any_day">أي يوم</string>
@@ -156,7 +157,22 @@
<string name="score">النقاط</string>
<string name="reminder_sound">صوت تذكير</string>
<string name="none">صامت</string>
<string name="filter">تصنيف</string>
<string name="hide_completed">إخفاء المكتملة</string>
<string name="hide_archived">إخفاء المؤرشفة</string>
<string name="sticky_notifications">جعل الإشعارات ثابتة</string>
<string name="sticky_notifications_description">منع الإشعارات من تمريرها بعيداً.</string>
<string name="repair_database">إصلاح قاعدة البيانات</string>
<string name="database_repaired">تم إصلاح قاعدة البيانات.</string>
<string name="uncheck">إلغاء تحديد</string>
<string name="toggle">تبديل</string>
<string name="action">عمل</string>
<string name="habit">عادة</string>
<string name="sort">فرز</string>
<string name="manually">يدوياً</string>
<string name="by_name">حسب الإسم</string>
<string name="by_color">حسب اللون</string>
<string name="by_score">حسب النقاط</string>
<string name="download">تحميل</string>
<string name="export">استخراج</string>
</resources>

View File

@@ -37,7 +37,7 @@
<string name="toast_habit_changed_back">Промяната на навика е отменена.</string>
<string name="toast_habit_archived">Навиците са архивирани</string>
<string name="toast_habit_unarchived">Навиците са разархивирани</string>
<string name="overview">Обобщение</string>
<string name="overview">Обзор</string>
<string name="habit_strength">Сила на навика</string>
<string name="history">История</string>
<string name="clear">Изчистване</string>
@@ -65,8 +65,8 @@
<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_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>

View File

@@ -57,7 +57,7 @@
<string name="validation_at_most_one_rep_per_day">Můžete mít maximálně jedno označení denně.</string>
<string name="create_habit">Vytvořit zvyk</string>
<string name="edit_habit">Upravit zvyk</string>
<string name="check">Označeno</string>
<string name="check">Hotovo</string>
<string name="snooze">Odložit</string>
<!-- App introduction -->
<string name="intro_title_1">Vítejte</string>

View File

@@ -48,26 +48,26 @@
<string name="reminder">Erinnerung</string>
<string name="discard">Verwerfen</string>
<string name="save">Speichern</string>
<string name="streaks">Strähnen</string>
<string name="streaks">Serien</string>
<string name="no_habits_found">Du hast keine aktiven Gewohnheiten</string>
<string name="long_press_to_toggle">Tippe und halte um aus- bzw. abzuwählen</string>
<string name="reminder_off">Aus</string>
<string name="validation_name_should_not_be_blank">Name darf nicht leer sein.</string>
<string name="validation_number_should_be_positive">Zahl muss positiv sein.</string>
<string name="validation_at_most_one_rep_per_day">Du musst wenigstens eine Wiederholung pro Tag haben</string>
<string name="validation_at_most_one_rep_per_day">Du kannst höchstens eine Wiederholung pro Tag haben</string>
<string name="create_habit">Gewohnheit erstellen</string>
<string name="edit_habit">Gewohnheit bearbeiten</string>
<string name="check">Abhaken</string>
<string name="check">Markieren</string>
<string name="snooze">Später</string>
<!-- App introduction -->
<string name="intro_title_1">Willkommen</string>
<string name="intro_description_1">Loop Habit Tracker hilft dir gute Gewohnheiten implementieren.</string>
<string name="intro_description_1">Loop Habit Tracker hilft dir dabei, gute Gewohnheiten anzunehmen.</string>
<string name="intro_title_2">Erstelle neue Gewohnheiten</string>
<string name="intro_description_2">Jeden Tag, nachdem du die Gewohnheit gemacht hast, hake sie in der App ab.</string>
<string name="intro_description_2">Hake die Gewohnheit jeden Tag in der App ab, nachdem du sie erledigt hast.</string>
<string name="intro_title_3">Bleib dran</string>
<string name="intro_description_3">Gewohnheiten, die über einen längeren Zeitraum absolviert werden, bekommen einen ganzen Stern.</string>
<string name="intro_title_4">Verfolge deinen Fortschritt</string>
<string name="intro_description_4">Detaillierte Diagramme zeigen dir an wie sich deine Gewohnheiten entwickelt haben.</string>
<string name="intro_description_4">Detaillierte Diagramme zeigen dir an, wie sich deine Gewohnheiten entwickelt haben.</string>
<string name="interval_15_minutes">15 Minuten</string>
<string name="interval_30_minutes">30 Minuten</string>
<string name="interval_1_hour">1 Stunde</string>
@@ -75,29 +75,29 @@
<string name="interval_4_hour">4 Stunden</string>
<string name="interval_8_hour">8 Stunden</string>
<string name="interval_24_hour">24 Stunden</string>
<string name="pref_toggle_title">Gewohnheiten durch kurzes Tippen abhaken</string>
<string name="pref_toggle_description">Abhaken durch einfaches Tippen, anstatt durch Tippen und Halten. Bequemer, kann aber eine falsche Auswahl verursachen.</string>
<string name="pref_toggle_title">Markierung durch kurzes Drücken ändern</string>
<string name="pref_toggle_description">Markierungen durch einfaches Tippen setzen anstatt durch Tippen und Halten. Bequemer, kann aber versehentlich eine Markierung ändern.</string>
<string name="pref_snooze_interval_title">\"Später erinnern\"-Intervall bei Erinnerungen</string>
<string name="pref_rate_this_app">Bewerte diese App auf Google Play</string>
<string name="pref_send_feedback">Sende dem Entwickler Feedback</string>
<string name="pref_view_source_code">Zeige den Quellcode auf GitHub</string>
<string name="pref_view_app_introduction">Zeige die Anleitung</string>
<string name="pref_view_app_introduction">Zeige die App-Einführung</string>
<string name="links">Links</string>
<string name="behavior">Verhalten</string>
<string name="name">Name</string>
<string name="settings">Einstellungen</string>
<string name="snooze_interval">\"Später erinnern\"-Intervall</string>
<string name="hint_title">Wusstest du?</string>
<string name="hint_drag">Um Einträge umzusortieren, tippe und ziehe sie an die richtige Stelle.</string>
<string name="hint_drag">Um Einträge umzusortieren, tippe, halte und ziehe sie an die richtige Stelle.</string>
<string name="hint_landscape">Du kannst mehr Tage sehen, wenn du dein Smartphone quer hältst.</string>
<string name="delete_habits">Gewohnheiten löschen</string>
<string name="delete_habits_message">Diese Gewohnheit wird permanent gelöscht. Dies kann nicht rückgängig gemacht werden.</string>
<string name="delete_habits">Lösche Gewohnheiten</string>
<string name="delete_habits_message">Die Gewohnheit wird für immer gelöscht. Dies kann nicht rückgängig gemacht werden.</string>
<string name="habit_not_found">Gewohnheit gelöscht / nicht gefunden</string>
<string name="weekends">An Wochenenden</string>
<string name="any_weekday">Werktags</string>
<string name="any_weekday">Montag bis Freitag</string>
<string name="any_day">Jeden Tag</string>
<string name="select_weekdays">Wähle die Tage</string>
<string name="export_to_csv">als CSV exportieren</string>
<string name="select_weekdays">Wähle Tage aus</string>
<string name="export_to_csv">Exportiere als CSV</string>
<string name="done_label">Fertig</string>
<string name="clear_label">Löschen</string>
<string name="select_hours">Wähle Stunden</string>
@@ -106,11 +106,11 @@
<string name="translators">Übersetzer</string>
<string name="developers">Entwickler</string>
<string name="version_n">Version %s</string>
<string name="frequency">Frequenz</string>
<string name="frequency">Häufigkeit</string>
<string name="checkmark">Häkchen</string>
<string name="strength">Stärke</string>
<string name="best_streaks">Beste Strähnen</string>
<string name="current_streaks">Derzeitige Strähne</string>
<string name="best_streaks">Beste Serien</string>
<string name="current_streaks">Derzeitige Serie</string>
<string name="number_of_repetitions">Anzahl der Wiederholungen</string>
<string name="last_x_days">Letzten %d Tage</string>
<string name="last_x_weeks">Letzten %d Wochen</string>
@@ -123,25 +123,25 @@
<string name="five_times_per_week">5 Mal pro Woche</string>
<string name="custom_frequency">Benutzerdefiniert</string>
<string name="help">Hilfe &amp; FAQ</string>
<string name="could_not_export">Der Export von Daten ist fehlgeschlagen.</string>
<string name="could_not_import">Der Import von Daten ist fehlgeschlagen.</string>
<string name="could_not_export">Fehler beim Exportieren der Daten.</string>
<string name="could_not_import">Fehler beim Importieren der Daten.</string>
<string name="file_not_recognized">Datei nicht erkannt.</string>
<string name="habits_imported">Gewohnheiten wurden erfolgreich importiert.</string>
<string name="habits_imported">Gewohnheiten erfolgreich importiert.</string>
<string name="full_backup_success">Vollständige Sicherung erfolgreich exportiert.</string>
<string name="import_data">Importiere Daten</string>
<string name="export_full_backup">Exportiere vollständige Sicherung</string>
<string name="import_data_summary">Unterstützt vollständige Sicherungen dieser App, sowie erstellte Sicherungen von Tickmate, HabitBull oder Rewire. Siehe FAQ für mehr Information.</string>
<string name="export_as_csv_summary">Erzeugt Dateien, die von Tabellenkalkulationsprogrammen, wie Microsoft Excel oder LibreOffice Calc, geöffnet werden können. Diese Dateien können nicht wieder importiert werden.</string>
<string name="export_full_backup_summary">Erzeugt eine Datei, die alle deine Daten enthält. Diese Datei kann wieder importiert werden.</string>
<string name="bug_report_failed">Fehlermeldung konnte nicht erstellt werden.</string>
<string name="generate_bug_report">Einen Fehler melden</string>
<string name="import_data_summary">Unterstützt vollständige Sicherungen dieser App sowie Sicherungen von Tickmate, HabitBull und Rewire. Siehe FAQ für weitere Informationen.</string>
<string name="export_as_csv_summary">Erstellt Dateien, die von Tabellenkalkulationsprogrammen wie Microsoft Excel oder LibreOffice Calc geöffnet werden können. Diese Dateien können nicht wieder importiert werden.</string>
<string name="export_full_backup_summary">Erstellt eine Datei, die alle deine Daten enthält. Diese Datei kann wieder importiert werden.</string>
<string name="bug_report_failed">Fehler beim Erstellen eines Fehlerberichts.</string>
<string name="generate_bug_report">Erstelle einen Fehlerbericht</string>
<string name="troubleshooting">Fehlerbehebung</string>
<string name="help_translate">Hilf diese App zu übersetzen</string>
<string name="help_translate">Hilf mit, diese App zu übersetzen</string>
<string name="night_mode">Nachtmodus</string>
<string name="use_pure_black">Reines Schwarz im Nachtmodus verwenden</string>
<string name="pure_black_description">Ersetzt das Grau im Hintergrund durch ein reines Schwarz im Nachtmodus. Reduziert den Stromverbrauch von Smartphones mit einem AMOLED Display.</string>
<string name="use_pure_black">Verwende reines Schwarz im Nachtmodus</string>
<string name="pure_black_description">Ersetzt im Nachtmodus das Grau im Hintergrund durch ein reines Schwarz. Reduziert den Stromverbrauch von Smartphones mit einem AMOLED Display.</string>
<string name="interface_preferences">Oberfläche</string>
<string name="reverse_days">Die Reihenfolge der Tage umkehren</string>
<string name="reverse_days">Kehre die Tagesreihenfolge um</string>
<string name="reverse_days_description">Zeigt die Tage im Hauptfenster in umgekehrter Reihenfolge an</string>
<string name="day">Tag</string>
<string name="week">Woche</string>
@@ -151,18 +151,18 @@
<string name="total">Gesamt</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">Mal in</string>
<string name="every_x_days">Jeden %d Tag</string>
<string name="every_x_weeks">Jede %d Woche</string>
<string name="every_x_months">Jeden %d Monat</string>
<string name="every_x_days">Alle %d Tage</string>
<string name="every_x_weeks">Alle %d Wochen</string>
<string name="every_x_months">Alle %d Monate</string>
<string name="score">Wertung</string>
<string name="reminder_sound">Benachrichtigungston</string>
<string name="none">Kein Ton</string>
<string name="reminder_sound">Erinnerungston</string>
<string name="none">Keiner</string>
<string name="filter">Filter</string>
<string name="hide_completed">Erledigte Gewohnheiten ausblenden</string>
<string name="hide_archived">Archivierte Gewohnheiten ausblenden</string>
<string name="sticky_notifications">Permanente Benachrichtigungen</string>
<string name="hide_completed">Abgeschlossene verbergen</string>
<string name="hide_archived">Archivierte verbergen</string>
<string name="sticky_notifications">Fixiere Benachrichtigungen</string>
<string name="sticky_notifications_description">Verhindert das Wegwischen von Benachrichtigungen.</string>
<string name="repair_database">Datenbank reparieren</string>
<string name="repair_database">Repariere Datenbank</string>
<string name="database_repaired">Datenbank repariert.</string>
<string name="uncheck">Abwählen</string>
<string name="toggle">Umschalten</string>
@@ -174,5 +174,5 @@
<string name="by_color">Nach Farbe</string>
<string name="by_score">Nach Wertung</string>
<string name="download">Runterladen</string>
<string name="export">Export</string>
<string name="export">Exportieren</string>
</resources>

View File

@@ -19,6 +19,76 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="main_activity_title">Kutimoj</string>
<string name="action_settings">Agordoj</string>
<string name="edit">Redakti</string>
<string name="delete">Forigi</string>
<string name="archive">Arĥivo</string>
<string name="unarchive">Elarĥivigi</string>
<string name="add_habit">Aldonu kutimon</string>
<string name="color_picker_default_title">Ŝanĝi koloron</string>
<string name="toast_habit_changed">Kutimo ŝanĝita</string>
<string name="toast_habit_archived">Kutimo arĥivita</string>
<string name="habit_strength">Kutimo forteco</string>
<string name="days">tagoj</string>
<string name="reminder">Memorigaĵoj</string>
<string name="discard">Nuligi</string>
<string name="save">Konservi</string>
<string name="streaks">Strioj</string>
<string name="reminder_off">Neaktiva</string>
<string name="snooze">Poste</string>
<!-- App introduction -->
<string name="intro_title_1">Bonvenon</string>
<string name="interval_15_minutes">15 minutoj</string>
<string name="interval_30_minutes">30 minutoj</string>
<string name="settings">Agordoj</string>
<string name="delete_habits">Forigi kutimojn</string>
<string name="weekends">Semajnfinoj</string>
<string name="any_weekday">Lundo al vendredo</string>
<string name="any_day">Io semajntago</string>
<string name="select_weekdays">Elekti tagojn</string>
<string name="export_to_csv">Eksporti kiel CSV</string>
<string name="done_label">Farite</string>
<string name="select_hours">Elekti horojn</string>
<string name="select_minutes">Elekti minutojn</string>
<string name="about">Pri programo</string>
<string name="translators">Tradukantoj</string>
<string name="developers">Evoluigantoj</string>
<string name="version_n">Versio %s</string>
<string name="frequency">Frekvenco</string>
<string name="strength">Forteco</string>
<string name="number_of_repetitions">Nombro de ripetoj</string>
<string name="last_x_days">Lastaj %d tagoj</string>
<string name="last_x_weeks">Lastaj %d semajnoj</string>
<string name="last_x_months">Lastaj %d monatoj</string>
<string name="last_x_years">Lastaj %d jaroj</string>
<string name="all_time">Ĉiuj tempoj</string>
<string name="every_day">Ĉiu tago</string>
<string name="every_week">Ĉiu semajno</string>
<string name="two_times_per_week">Dufoje en semajno</string>
<string name="five_times_per_week">Kvinfoje en semajno</string>
<string name="help">Helpo &amp; Ofte Demandite</string>
<string name="file_not_recognized">Dosiero ne rekonita.</string>
<string name="full_backup_success">Plena savkopio sukcese eksportita.</string>
<string name="troubleshooting">Problemserĉado</string>
<string name="night_mode">Nokta reĝimo</string>
<string name="day">Tago</string>
<string name="week">Semajno</string>
<string name="month">Monato</string>
<string name="quarter">Jarkvarono</string>
<string name="year">Jaro</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="none">Nenio</string>
<string name="filter">Filtrilo</string>
<string name="hide_completed">Kaŝi kompletajn</string>
<string name="hide_archived">Kaŝi arĥivitajn</string>
<string name="repair_database">Ripari datumbazon</string>
<string name="database_repaired">Datumbazon riparita.</string>
<string name="action">Ago</string>
<string name="habit">Kutimo</string>
<string name="sort">Enkursigi</string>
<string name="by_name">Laŭ nomo</string>
<string name="by_color">Laŭ koloro</string>
<string name="download">Elŝuti</string>
<string name="export">Eksporti</string>
</resources>

View File

@@ -28,20 +28,20 @@
<string name="unarchive">Desarchivar</string>
<string name="add_habit">Agregar hábito</string>
<string name="color_picker_default_title">Cambiar color</string>
<string name="toast_habit_created">Hábito creado.</string>
<string name="toast_habit_deleted">Hábitos eliminados.</string>
<string name="toast_habit_restored">Hábitos restaurados.</string>
<string name="toast_nothing_to_undo">Nada que deshacer.</string>
<string name="toast_nothing_to_redo">Nada que rehacer.</string>
<string name="toast_habit_changed">Hábito cambiado.</string>
<string name="toast_habit_changed_back">bito cambiado nuevamente.</string>
<string name="toast_habit_archived">Hábitos archivados.</string>
<string name="toast_habit_unarchived">Hábitos desarchivados.</string>
<string name="toast_habit_created">Hábito creado</string>
<string name="toast_habit_deleted">Hábitos eliminados</string>
<string name="toast_habit_restored">Hábitos restaurados</string>
<string name="toast_nothing_to_undo">Nada que deshacer</string>
<string name="toast_nothing_to_redo">Nada que rehacer</string>
<string name="toast_habit_changed">Hábito cambiado</string>
<string name="toast_habit_changed_back">Cambio en hábito vuelto atrás</string>
<string name="toast_habit_archived">Hábitos archivados</string>
<string name="toast_habit_unarchived">Hábitos desarchivados</string>
<string name="overview">Resumen</string>
<string name="habit_strength">Fuerza del hábito</string>
<string name="history">Historial</string>
<string name="clear">Eliminar</string>
<string name="description_hint">Pregunta (Has ___ hoy?)</string>
<string name="clear">Borrar</string>
<string name="description_hint">Pregunta (Has ... hoy?)</string>
<string name="repeat">Repetir</string>
<string name="times_every">veces cada</string>
<string name="days">días</string>
@@ -64,7 +64,7 @@
<string name="intro_description_1">Loop Analizador de Hábitos te ayuda a crear y mantener buenos hábitos.</string>
<string name="intro_title_2">Crea algunos hábitos nuevos</string>
<string name="intro_description_2">Cada día, después de realizar tu hábito, pon una marca en la aplicación.</string>
<string name="intro_title_3">Sigue haciéndolo.</string>
<string name="intro_title_3">Sigue haciéndolo</string>
<string name="intro_description_3">Los hábitos realizados consistentemente por un largo tiempo ganarán una estrella completa.</string>
<string name="intro_title_4">Haz un seguimiento de tu progreso</string>
<string name="intro_description_4">Gráficos detallados muestran cómo mejoraron sus hábitos con el tiempo.</string>
@@ -75,9 +75,9 @@
<string name="interval_4_hour">4 horas</string>
<string name="interval_8_hour">8 horas</string>
<string name="interval_24_hour">24 horas</string>
<string name="pref_toggle_title">Marca las repeticiones con una pulsación corta.</string>
<string name="pref_toggle_title">Marca las repeticiones con una pulsación corta</string>
<string name="pref_toggle_description">Más cómodo, pero puede causar marcas accidentales.</string>
<string name="pref_snooze_interval_title">Tiempo de espera al aplazar recordatorios.</string>
<string name="pref_snooze_interval_title">Tiempo de espera al aplazar recordatorios</string>
<string name="pref_rate_this_app">Valora esta aplicación en Google Play</string>
<string name="pref_send_feedback">Enviar sugerencias al desarrollador</string>
<string name="pref_view_source_code">Ver código fuente en GitHub</string>
@@ -88,20 +88,20 @@
<string name="settings">Configuración</string>
<string name="snooze_interval">Intervalo de espera</string>
<string name="hint_title">¿Sabías qué?</string>
<string name="hint_drag">Para reordenar las entradas, mantén la pulsación sobre el nombre del hábito, después arrástralo a su posición correcta.</string>
<string name="hint_drag">Para reordenar las entradas, mantén la pulsado sobre el nombre del hábito, después arrástralo a su posición correcta.</string>
<string name="hint_landscape">Puedes ver más días al poner tu teléfono en modo horizontal.</string>
<string name="delete_habits">Eliminar Hábitos</string>
<string name="delete_habits_message">Los hábitos serán eliminados permanentemente. Esta acción no se puede deshacer.</string>
<string name="habit_not_found">Hábito eliminado / no encontrado</string>
<string name="weekends">Fines de semana</string>
<string name="any_weekday">Días laborables</string>
<string name="any_weekday">De lunes a viernes</string>
<string name="any_day">Cada día</string>
<string name="select_weekdays">Seleccionar días</string>
<string name="export_to_csv">Exportar datos (CSV)</string>
<string name="done_label">Hecho</string>
<string name="clear_label">Quitar</string>
<string name="select_hours">Seleccionar horas</string>
<string name="select_minutes">Seleccionar</string>
<string name="select_minutes">Seleccionar minutos</string>
<string name="about">Acerca de</string>
<string name="translators">Traductores</string>
<string name="developers">Desarrolladores</string>

View File

@@ -19,23 +19,24 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">Loop Habit Tracker</string>
<string name="main_activity_title">Ohiturak</string>
<string name="action_settings">Ezarpenak</string>
<string name="edit">Editatu</string>
<string name="delete">Ezabatu</string>
<string name="archive">Artxibatu</string>
<string name="unarchive">Ezartxibatu</string>
<string name="add_habit">Ohitura gehitu</string>
<string name="unarchive">Desartxibatu</string>
<string name="add_habit">Gehitu ohitura</string>
<string name="color_picker_default_title">Kolorea aldatu</string>
<string name="toast_habit_created">Ohitura sortu da.</string>
<string name="toast_habit_deleted">Ohiturak ezabatu dira.</string>
<string name="toast_habit_restored">Ohiturak berrezarri dira.</string>
<string name="toast_nothing_to_undo">Ez dago desegiteko ezer.</string>
<string name="toast_nothing_to_redo">Ez dago berregiteko ezer.</string>
<string name="toast_habit_changed">Ohitura aldatu da.</string>
<string name="toast_habit_changed_back">Ohitura lehengoratu da.</string>
<string name="toast_habit_archived">Ohiturak artxibatu dira.</string>
<string name="toast_habit_unarchived">Ohiturak ezartxibatu dira.</string>
<string name="toast_habit_created">Ohitura sortu da</string>
<string name="toast_habit_deleted">Ohiturak ezabatu dira</string>
<string name="toast_habit_restored">Ohiturak berrezarri dira</string>
<string name="toast_nothing_to_undo">Ez dago ezer desegiteko</string>
<string name="toast_nothing_to_redo">Ez dago ezer berregiteko</string>
<string name="toast_habit_changed">Ohitura aldatu egin da</string>
<string name="toast_habit_changed_back">Ohitura berrezarri da</string>
<string name="toast_habit_archived">Ohiturak artxibatu dira</string>
<string name="toast_habit_unarchived">Ohiturak desartxibatu dira</string>
<string name="overview">Ikuspegi orokorra</string>
<string name="habit_strength">Ohituraren indarra</string>
<string name="history">Historia</string>
@@ -57,10 +58,10 @@
<string name="create_habit">Ohitura sortu</string>
<string name="edit_habit">Ohitura editatu</string>
<string name="check">Markatu</string>
<string name="snooze">Beranduago</string>
<string name="snooze">Geroago</string>
<!-- App introduction -->
<string name="intro_title_1">Ongi etorri</string>
<string name="intro_description_1">Loop Habit Trackerek ohitura onak hartzen eta mantentzen laguntzen dizu.</string>
<string name="intro_description_1">Loop Habit Tracker-ek ohitura onak hartzen eta mantentzen laguntzen dizu.</string>
<string name="intro_title_2">Sor itzazu ohitura berri batzuk</string>
<string name="intro_description_2">Egunero, zure ohitura egin ostean, jarri ezazu egiaztatze marka bat aplikazioan.</string>
<string name="intro_title_3">Jarrai ezazu ohitura egiten</string>
@@ -76,7 +77,7 @@
<string name="interval_24_hour">24 ordu</string>
<string name="pref_toggle_title">Ukitze laburrarekin markatu</string>
<string name="pref_toggle_description">Ukitze bakar batekin marka jartzen du ukitu eta mantendu egin beharrean. Erosoagoa, baina nahi gabeko markak ekar litzake.</string>
<string name="pref_snooze_interval_title">Atzeratze tartea gogorarazpenetan</string>
<string name="pref_snooze_interval_title">Atzeratze tartea oroigarrietan</string>
<string name="pref_rate_this_app">Aplikazio hau Google Playen puntuatu</string>
<string name="pref_send_feedback">Zure iritzia garatzaileari bidali</string>
<string name="pref_view_source_code">Iturburu kodea GitHuben ikusi</string>
@@ -149,6 +150,7 @@
<string name="year">Urtea</string>
<string name="total">Guztira</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">denbora</string>
<string name="every_x_days">%d egunero</string>
<string name="every_x_weeks">%d astero</string>
<string name="every_x_months">%d hilabetero</string>
@@ -156,7 +158,7 @@
<string name="reminder_sound">Oroigarriaren soinua</string>
<string name="none">Bat ere ez</string>
<string name="filter">Iragazkia</string>
<string name="hide_completed">Lortutakoak ezkutatu</string>
<string name="hide_completed">Ezkutatu lortutakoak</string>
<string name="hide_archived">Artxibatutakoak ezkutatu</string>
<string name="sticky_notifications">Jakinarazpenak itsaskorrak bihurtu</string>
<string name="sticky_notifications_description">Jakinarazpenak keinu batez ezabatzea sahiesten du.</string>

View File

@@ -19,34 +19,34 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">عادت‌سنج</string>
<string name="app_name">Loop Habit Tracker</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="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_habit_restored">عادت بازگردانده شد.</string>
<string name="toast_nothing_to_undo">چیزی برای واکردن وجود ندارد.</string>
<string name="toast_nothing_to_redo">چیزی برای برگشت وجود ندارد.</string>
<string name="toast_habit_created">عادت ایجاد شد</string>
<string name="toast_habit_deleted">عادت حذف شد</string>
<string name="toast_habit_restored">عادت بازگردانده شد</string>
<string name="toast_nothing_to_undo">چیزی برای بازگرداندن به حالت قبلی وجود ندارد</string>
<string name="toast_nothing_to_redo">چیزی برای انجام مجدد وجود ندارد</string>
<string name="toast_habit_changed">عادت تغییر کرد.</string>
<string name="toast_habit_changed_back">عادت به حالت قبل برگشت.</string>
<string name="toast_habit_archived">عادت آرشیو شد.</string>
<string name="toast_habit_unarchived">عادت از آرشیو خارج شد.</string>
<string name="toast_habit_changed_back">عادت به حالت قبل برگشت</string>
<string name="toast_habit_archived">عادتها بایگانی شدند</string>
<string name="toast_habit_unarchived">عادتها از بایگانی خارج شدند</string>
<string name="overview">مرور</string>
<string name="habit_strength">قوت عادت</string>
<string name="habit_strength">قدرت عادت</string>
<string name="history">تاریخچه</string>
<string name="clear">حذف</string>
<string name="clear">بی‌خیال</string>
<string name="description_hint">سوال (آیا امروز شما&#8230;؟)</string>
<string name="repeat">تکرار</string>
<string name="times_every">بار در هر</string>
<string name="days">روزها</string>
<string name="days">روز</string>
<string name="reminder">یادآور</string>
<string name="discard">حذف تغییرات</string>
<string name="discard">بی‌خیال</string>
<string name="save">ذخیره</string>
<string name="streaks">روزهای پیوسته</string>
<string name="no_habits_found">شما هیچ عادت فعالی ندارید</string>
@@ -55,18 +55,18 @@
<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="create_habit">درج عادت جدید</string>
<string name="edit_habit">ویرایش عادت</string>
<string name="check">تیک زدن</string>
<string name="snooze">بعدا</string>
<string name="snooze">بعداً</string>
<!-- App introduction -->
<string name="intro_title_1">خوش آمدید</string>
<string name="intro_description_1">رهگیر عادت به شما کمک می‌کند تا برای خودتان عادت‌های خوبی بسازید.</string>
<string name="intro_description_1">رهگیر عادت لوپ به شما کمک می‌کند تا برای خودتان عادت‌های خوبی بسازید.</string>
<string name="intro_title_2">ساخت چند عادت جدید</string>
<string name="intro_description_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_title_4">پیشرفت خود را رهگیری کنید</string>
<string name="intro_description_4">نمودار جزئیات به شما نشان می‌دهد که چطور عادت‌هایتان با گذشت زمان بهبود پیدا کرده‌اند.</string>
<string name="interval_15_minutes">۱۵ دقیقه</string>
<string name="interval_30_minutes">۳۰ دقیقه</string>
@@ -74,30 +74,40 @@
<string name="interval_2_hour">۲ ساعت</string>
<string name="interval_4_hour">۴ ساعت</string>
<string name="interval_8_hour">۸ ساعت</string>
<string name="interval_24_hour">۲۴ ساعت</string>
<string name="pref_toggle_title">با اشاره‌ی کوتاه‌مدت وضعیت عادت را تغییر بده</string>
<string name="pref_toggle_description">راحت‌تر است ولی ممکن است باعث شود اشتباهی عادتی را تیک بزنید.</string>
<string name="pref_rate_this_app">دادن امتیاز به اپ در گوگل‌پلی</string>
<string name="pref_toggle_description">تیک زدن با تک‌ضربه در مقابل ضربه‌زدن و نگه‌داشتن راحت‌تر است ولی ممکن است باعث شود اشتباهی عادتی را تیک بزنید.</string>
<string name="pref_snooze_interval_title">بازه به تعویق انداختن یادآورها</string>
<string name="pref_rate_this_app">به این برنامه در گوگل‌پلی امتیاز بدهید</string>
<string name="pref_send_feedback">ارسال بازخورد به توسعه‌دهنده</string>
<string name="pref_view_source_code">دیدن سورس اپ در گیت‌هاب</string>
<string name="pref_view_app_introduction">دیدن معرفی اپ</string>
<string name="pref_view_source_code">دیدن منبع برنامه در گیت‌هاب</string>
<string name="pref_view_app_introduction">مشاهده معرفی برنامه</string>
<string name="links">لینک‌ها</string>
<string name="behavior">رفتار</string>
<string name="name">نام</string>
<string name="settings">تنظیمات</string>
<string name="hint_title">آیا می‌دانید؟</string>
<string name="snooze_interval">بازه به تعویق انداختن</string>
<string name="hint_title">آیا می دانستید؟</string>
<string name="hint_drag">برای جابجایی عناوین، انگشتتان را روی نام عادت مورد نظر بگذارید و نگه دارید، سپس آن را به محل صحیح بکشید.</string>
<string name="hint_landscape">با قرار دادن گوشی در حالت افقی می‌توانید روزهای بیشتری را ببینید.</string>
<string name="delete_habits">حذف عادت‌ها</string>
<string name="delete_habits_message">عادت‌ها برای همیشه حذف خواهد شد. این عمل قابل بازگشت نیست.</string>
<string name="habit_not_found">عادت حذف شده/ پیدا نشد</string>
<string name="weekends">آخر هفته‌ها</string>
<string name="any_weekday">دوشنبه تا جمعه</string>
<string name="any_day">هر روز از هفته</string>
<string name="any_day">هر روز هفته</string>
<string name="select_weekdays">انتخاب روزها</string>
<string name="export_to_csv">دخیره به عنوان فایل CSV</string>
<string name="done_label">انجام شده</string>
<string name="clear_label">حذف</string>
<string name="select_hours">انتخاب ساعتها</string>
<string name="export_to_csv">صدور فایل CSV</string>
<string name="done_label">انجام شد</string>
<string name="clear_label">بی‌خیال</string>
<string name="select_hours">انتخاب ساعت</string>
<string name="select_minutes">انتخاب دقیقه</string>
<string name="about">درباره</string>
<string name="translators">مترجمین</string>
<string name="translators">مترجمان</string>
<string name="developers">توسعه‌دهندگان</string>
<string name="version_n">نسخه %s</string>
<string name="frequency">تناوب</string>
<string name="checkmark">علامت</string>
<string name="strength">قدرت</string>
<string name="best_streaks">بهترین استمرار</string>
<string name="current_streaks">استمرار فعلی</string>
@@ -106,20 +116,39 @@
<string name="last_x_weeks">%d هفته اخیر</string>
<string name="last_x_months">%d ماه اخیر</string>
<string name="last_x_years">%d سال اخیر</string>
<string name="all_time">همیشه</string>
<string name="every_day">هر روز</string>
<string name="every_week">هر هفته</string>
<string name="two_times_per_week">۲ بار در هفته</string>
<string name="five_times_per_week">۵ بار در هفته</string>
<string name="custom_frequency">سفارشی‌سازی ...</string>
<string name="help">راهنما و سوالات متداول</string>
<string name="could_not_export">خطا در صدور اطلاعات.</string>
<string name="could_not_import">خطا در وارد کردن اطلاعات.</string>
<string name="file_not_recognized">پرونده شناخته شده نیست.</string>
<string name="habits_imported">عادت‌ها با موفقیت وارد شدند.</string>
<string name="full_backup_success">پرونده پشتیبان کامل، با موفقیت صادر شد.</string>
<string name="import_data">ورود اطلاعات</string>
<string name="export_full_backup">پشتیبان گیری کامل</string>
<string name="import_data_summary">علاوه بر پشتیبان کامل تهیه شده توسط این برنامه، از پرونده‌های تولید شده توسط Tickmate، HabitbBull و یا Rewire هم پشتیبانی می‌شود. برای اطلاعات بیشتر سوالات متداول را ببینید.</string>
<string name="export_as_csv_summary">پرونده‌ای تولید می‌کند که می‌توان توسط برنامه‌های صفحه گسترده مانند Microsoft Excel و یا OpenOffice Calc بازشان کرد. این پرونده قابلیت وارد کردن مجدد به این برنامه را ندارد.</string>
<string name="export_full_backup_summary">پرونده‌ای تولید می‌کند که شامل تمام اطلاعات شما است. این پرونده قابل بازیابی توسط این برنامه می‌باشد.</string>
<string name="bug_report_failed">خطایی در تولید گزارش مشکلات بوجود آمد.</string>
<string name="generate_bug_report">ایجاد گزارش مشکلات</string>
<string name="troubleshooting">ایرادیابی</string>
<string name="night_mode">حالت شبانه</string>
<string name="help_translate">کمک برای ترجمه این برنامه</string>
<string name="night_mode">حالت شب</string>
<string name="use_pure_black">استفاده از رنگ سیاه خالص در حالت شبانه</string>
<string name="pure_black_description">جایگزینی پس زمینه خاکستری با سیاه خالص در حالت شب. استفاده از باتری در گوشی‌های با صفحه نمایش AMOLED را کاهش می‌دهد.</string>
<string name="interface_preferences">رابط کاربری</string>
<string name="reverse_days">معکوس کردن ترتیب روزها</string>
<string name="reverse_days_description">روزها را در صفحه اصلی با ترتیب معکوس نمایش می‌دهد</string>
<string name="day">روز</string>
<string name="week">هفته</string>
<string name="month">ماه</string>
<string name="quarter">فصل</string>
<string name="year">سال</string>
<string name="total">مجموع</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">بار در هر</string>
<string name="every_x_days">هر %d روز یک‌بار</string>
@@ -128,4 +157,22 @@
<string name="score">امتیاز</string>
<string name="reminder_sound">صدای یادآور</string>
<string name="none">هیچ‌کدام</string>
<string name="filter">فیلتر</string>
<string name="hide_completed">مخفی کردن کامل شده‌ها</string>
<string name="hide_archived">مخفی کردن بایگانی شده‌ها</string>
<string name="sticky_notifications">چسبناک کردن اعلان‌ها</string>
<string name="sticky_notifications_description">از رد کردن اعلان با کشیدن جلوگیری می‌کند.</string>
<string name="repair_database">تعمیر پایگاه داده</string>
<string name="database_repaired">پایگاه داده تعمیر شد.</string>
<string name="uncheck">برداشتن تیک</string>
<string name="toggle">تغییر وضعیت</string>
<string name="action">اقدام</string>
<string name="habit">عادت</string>
<string name="sort">مرتب‌سازی</string>
<string name="manually">دستی</string>
<string name="by_name">بر اساس نام</string>
<string name="by_color">بر اساس رنگ</string>
<string name="by_score">بر اساس امتیاز</string>
<string name="download">بارگيری</string>
<string name="export">صدور</string>
</resources>

View File

@@ -19,6 +19,65 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">Rutiini - Tracker</string>
<string name="main_activity_title">Rutiinit</string>
<string name="action_settings">Asetukset</string>
<string name="edit">Muokkaa</string>
<string name="delete">Poista</string>
<string name="archive">Arkistoi</string>
<string name="add_habit">Lisää rutiini</string>
<string name="color_picker_default_title">Vaihda väriä</string>
<string name="toast_habit_created">Rutiini luotu</string>
<string name="toast_habit_deleted">Rutiinit poistettu</string>
<string name="toast_habit_restored">Rutiinit palautettu</string>
<string name="toast_habit_changed">Rutiini muutettu</string>
<string name="toast_habit_changed_back">Rutiini muutettu takaisin</string>
<string name="overview">Yleiskatsaus</string>
<string name="history">Historia</string>
<string name="clear">Tyhjennä</string>
<string name="description_hint">Kysymys (Teitkö... tänään?)</string>
<string name="repeat">Toista</string>
<string name="times_every">kertaa</string>
<string name="days">päivässä</string>
<string name="reminder">Muistutus</string>
<string name="discard">Hylkää</string>
<string name="save">Tallenna</string>
<string name="streaks">Pisimmät toistot</string>
<string name="no_habits_found">Ei aktiivisia rutiineja</string>
<string name="long_press_to_toggle">Paina pitkään merkitäksesi suoritetuksi tai postaaksesi suorituksen</string>
<string name="reminder_off">Pois päältä</string>
<string name="validation_name_should_not_be_blank">Nimi ei voi olla tyhjä.</string>
<string name="validation_number_should_be_positive">Luvun on oltava positiivinen.</string>
<string name="create_habit">Luo rutiini</string>
<string name="edit_habit">Muokkaa rutiinia</string>
<string name="check">Tehty</string>
<string name="snooze">Lykkää</string>
<!-- App introduction -->
<string name="intro_title_1">Tervetuloa</string>
<string name="intro_title_2">Merkitse uusia rutiineja</string>
<string name="intro_description_2">Joka päivä, suoritettuasi rutiinin, merkitse se sovellukseen.</string>
<string name="links">Linkit</string>
<string name="behavior">Käyttäytyminen</string>
<string name="name">Nimi</string>
<string name="settings">Asetukset</string>
<string name="hint_title">Tiesitkö?</string>
<string name="done_label">Valmis</string>
<string name="clear_label">Tyhjennä</string>
<string name="translators">Kääntäjät</string>
<string name="developers">Kehittäjät</string>
<string name="version_n">Versio %s</string>
<string name="every_day">Joka päivä</string>
<string name="every_week">Joka viikko</string>
<string name="two_times_per_week">2 kertaa viikossa</string>
<string name="five_times_per_week">5 kertaa viikossa</string>
<string name="custom_frequency">Mukautettu&#8230;</string>
<string name="night_mode">Yötila</string>
<string name="use_pure_black">Käytä puhdasta mustaa yötilassa</string>
<string name="day">Päivä</string>
<string name="week">Viikko</string>
<string name="month">Kuukausi</string>
<string name="quarter">Kvartaali</string>
<string name="year">Vuosi</string>
<string name="total">Yhteensä</string>
<!-- Middle part of the sentence '1 time in xx days' -->
</resources>

View File

@@ -29,8 +29,8 @@
<string name="add_habit">Ajouter une habitude</string>
<string name="color_picker_default_title">Changer la couleur</string>
<string name="toast_habit_created">Habitude créée</string>
<string name="toast_habit_deleted">Habitude supprimée</string>
<string name="toast_habit_restored">Habitude rétablie</string>
<string name="toast_habit_deleted">Habitudes supprimées</string>
<string name="toast_habit_restored">Habitudes rétablies</string>
<string name="toast_nothing_to_undo">Rien à annuler</string>
<string name="toast_nothing_to_redo">Rien à refaire</string>
<string name="toast_habit_changed">Habitude changée</string>
@@ -75,8 +75,8 @@
<string name="interval_4_hour">4 heures</string>
<string name="interval_8_hour">8 heures</string>
<string name="interval_24_hour">24 heures</string>
<string name="pref_toggle_title">Activer les répétitions avec un appui court</string>
<string name="pref_toggle_description">Pointe l\'habitude avec un appui court plutôt qu\'un appuie long. Plus pratique, mais peut causer des activations accidentelles.</string>
<string name="pref_toggle_title">Valider l\'habitude avec un appui court</string>
<string name="pref_toggle_description">Valide l\'habitude avec un appui court plutôt qu\'un appuie long. Plus pratique, mais peut causer des activations accidentelles.</string>
<string name="pref_snooze_interval_title">Intervalle de report des rappels</string>
<string name="pref_rate_this_app">Notez cette app sur le Google Play Store</string>
<string name="pref_send_feedback">Envoyez un avis au développeur</string>
@@ -91,10 +91,10 @@
<string name="hint_drag">Pour réordonner les habitudes, faites un appui long sur le nom de l\'habitude et placez-la à la bonne place.</string>
<string name="hint_landscape">Vous pouvez voir plus de jours en mettant votre téléphone en mode paysage.</string>
<string name="delete_habits">Supprimer des habitudes</string>
<string name="delete_habits_message">Les habitudes seront supprimées définitivement. Cette action ne peut être annulée.</string>
<string name="delete_habits_message">Les habitudes seront supprimées définitivement. Cette action est irréversible.</string>
<string name="habit_not_found">Habitude supprimée / introuvable</string>
<string name="weekends">Fin de semaine</string>
<string name="any_weekday">Jours de la semaine</string>
<string name="weekends">Weekends</string>
<string name="any_weekday">Du lundi au vendredi</string>
<string name="any_day">N\'importe quel jour</string>
<string name="select_weekdays">Sélectionner des jours</string>
<string name="export_to_csv">Exporter les données dans un fichier CSV</string>
@@ -107,7 +107,7 @@
<string name="developers">Développeurs</string>
<string name="version_n">Version %s</string>
<string name="frequency">Fréquence</string>
<string name="checkmark">Croix</string>
<string name="checkmark">Case à cocher</string>
<string name="strength">Force</string>
<string name="best_streaks">Meilleures séries</string>
<string name="current_streaks">Série actuelle</string>
@@ -131,7 +131,7 @@
<string name="import_data">Importer des données</string>
<string name="export_full_backup">Exporter une sauvegarde complète</string>
<string name="import_data_summary">Supporte les sauvegardes complètes générées par cette application, ainsi que les fichiers Tickmate, HabitBull et Rewire. Voir la FAQ pour plus d\'informations.</string>
<string name="export_as_csv_summary">Génère des fichiers pouvant être ouverts par des tableurs comme Microsoft Excel ou LibreOffice Calc. Ces fichiers ne peuvent être réimportés.</string>
<string name="export_as_csv_summary">Génère des fichiers pouvant être ouverts par des tableurs comme Microsoft Excel ou LibreOffice Calc. Ce fichier ne peut pas être réimporté.</string>
<string name="export_full_backup_summary">Génère un fichier contenant toutes vos données. Ce fichier peut être réimporté.</string>
<string name="bug_report_failed">La génération du rapport de bug a échouée.</string>
<string name="generate_bug_report">Générer un rapport de bug.</string>
@@ -154,7 +154,7 @@
<string name="every_x_days">Tous les %d jours</string>
<string name="every_x_weeks">Toutes les %d semaines</string>
<string name="every_x_months">Tous les %d mois</string>
<string name="score">Pointage</string>
<string name="score">Score</string>
<string name="reminder_sound">Son de rappel</string>
<string name="none">Aucun</string>
<string name="filter">Filtre</string>
@@ -162,7 +162,7 @@
<string name="hide_archived">Cacher les habitudes archivées</string>
<string name="sticky_notifications">Rendre les notifications persistantes</string>
<string name="sticky_notifications_description">Évite que les notifications ne soient enlevées.</string>
<string name="repair_database">Réparer le base de données</string>
<string name="repair_database">Réparer la base de données</string>
<string name="database_repaired">Base de données réparée.</string>
<string name="uncheck">Décocher</string>
<string name="toggle">Basculer</string>

View File

@@ -1,158 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.com-->
<!--
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
~
~ This file is part of Loop Habit Tracker.
~
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by the
~ Free Software Foundation, either version 3 of the License, or (at your
~ option) any later version.
~
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">Loop Habit Tracker</string>
<string name="main_activity_title">Kebiasaan</string>
<string name="action_settings">Pengaturan</string>
<string name="edit">Ubah</string>
<string name="delete">Hapus</string>
<string name="archive">Arsip</string>
<string name="unarchive">Keluarkan dari arsip</string>
<string name="add_habit">Tambah Kebiasaan</string>
<string name="color_picker_default_title">Ganti warna</string>
<string name="toast_habit_created">Kebiasaan ditambahkan.</string>
<string name="toast_habit_deleted">Kebiasaan dihapus.</string>
<string name="toast_habit_restored">Kebiasaan dikembalikan.</string>
<string name="toast_nothing_to_undo">Tidak ada aksi sebelumnya.</string>
<string name="toast_nothing_to_redo">Tidak ada aksi sesudahnya.</string>
<string name="toast_habit_changed">Kebiasaan diubah.</string>
<string name="toast_habit_changed_back">Kebiasaan telah dikembalikan.</string>
<string name="toast_habit_archived">Kebiasaan diarsipkan.</string>
<string name="toast_habit_unarchived">Kebiasaan dikeluarkan dari arsip.</string>
<string name="overview">Keseluruhan</string>
<string name="habit_strength">Kekuatan Kebiasaan</string>
<string name="history">Riwayat</string>
<string name="clear">Bersihkan</string>
<string name="description_hint">Pertanyaan (Apakah kamu ... hari ini?)</string>
<string name="repeat">Ulangi</string>
<string name="times_every">kali dalam</string>
<string name="days">hari</string>
<string name="reminder">Pengingat</string>
<string name="discard">Batalkan</string>
<string name="save">Simpan</string>
<string name="streaks">Rentetan</string>
<string name="no_habits_found">Anda tidak memiliki Kebiasaan yang aktif</string>
<string name="long_press_to_toggle">Tekan dan tahan untuk menambah atau menghapus tanda cek</string>
<string name="reminder_off">Mati</string>
<string name="validation_name_should_not_be_blank">Nama tidak boleh kosong.</string>
<string name="validation_number_should_be_positive">Angka harus positif.</string>
<string name="validation_at_most_one_rep_per_day">Maksimal satu kali pengulangan dalam satu hari</string>
<string name="create_habit">Buat Kebiasaan</string>
<string name="edit_habit">Ubah Kebiasaan</string>
<string name="check">Tanda cek</string>
<string name="snooze">Tunda</string>
<!-- App introduction -->
<string name="intro_title_1">Selamat datang</string>
<string name="intro_description_1">Loop Habit Tracker membantu mencatat dan mengelola Kebiasaan baik Anda.</string>
<string name="intro_title_2">Buat beberapa Kebiasaan baru</string>
<string name="intro_description_2">Berikan tanda cek setiap kali Anda selesai melakukannya.</string>
<string name="intro_title_3">Terus lakukan</string>
<string name="intro_description_3">Kebiasaan yang dilakukan secara konsisten dalam jangka waktu panjang akan mendapatkan tanda bintang penuh.</string>
<string name="intro_title_4">Catat perkembangan Anda</string>
<string name="intro_description_4">Detail grafik menampilkan perkembangan Kebiasaanmu dari waktu ke waktu.</string>
<string name="interval_15_minutes">15 menit</string>
<string name="interval_30_minutes">30 menit</string>
<string name="interval_1_hour">1 jam</string>
<string name="interval_2_hour">2 jam</string>
<string name="interval_4_hour">4 jam</string>
<string name="interval_8_hour">8 jam</string>
<string name="interval_24_hour">24 jam</string>
<string name="pref_toggle_title">Tandai dengan cepat.</string>
<string name="pref_toggle_description">Lebih nyaman namun memungkinkan kesalahan.</string>
<string name="pref_snooze_interval_title">Durasi tunda sejenak pada pengingat</string>
<string name="pref_rate_this_app">Berikan rating aplikasi ini di Google Play</string>
<string name="pref_send_feedback">Kirimkan umpan balik kepada Developer</string>
<string name="pref_view_source_code">Lihat kode aplikasi di GitHub</string>
<string name="pref_view_app_introduction">Perkenalan aplikasi</string>
<string name="links">Tautan</string>
<string name="behavior">Kebiasaan</string>
<string name="name">Nama</string>
<string name="settings">Pengaturan</string>
<string name="snooze_interval">Durasi tunda sejenak</string>
<string name="hint_title">Tahukah kamu?</string>
<string name="hint_drag">Untuk mengatur urutan, tekan dan tahan judul Kebiasaan lalu tempatkan pada posisi yang Anda inginkan.</string>
<string name="hint_landscape">Anda dapat melihat tampilan hari dengan merubah posisi menjadi mode landscape.</string>
<string name="delete_habits">Hapus Kebiasaan</string>
<string name="delete_habits_message">Kebiasaan ini akan dihapus permanen. Tidak dapat dikembalikan.</string>
<string name="weekends">Akhir pekan</string>
<string name="any_weekday">Senin - Jumat</string>
<string name="any_day">Seluruh hari dalam satu minggu</string>
<string name="select_weekdays">Pilih hari</string>
<string name="export_to_csv">Ekspor (CSV)</string>
<string name="done_label">Selesai</string>
<string name="clear_label">Hapus</string>
<string name="select_hours">Pilih jam</string>
<string name="select_minutes">Pilih menit</string>
<string name="about">Tentang</string>
<string name="translators">Penerjemah</string>
<string name="developers">Developer</string>
<string name="version_n">Versi %s</string>
<string name="frequency">Frekuensi</string>
<string name="checkmark">Cek</string>
<string name="strength">Kekuatan</string>
<string name="best_streaks">Rentetan terbaik</string>
<string name="current_streaks">Rentetan saat ini</string>
<string name="number_of_repetitions">Jumlah pengulangan</string>
<string name="last_x_days">%d hari terakhir</string>
<string name="last_x_weeks">%d minggu terakhir</string>
<string name="last_x_months">%d bulan terakhir</string>
<string name="last_x_years">%d tahun terakhir</string>
<string name="all_time">Seluruh waktu</string>
<string name="every_day">Setiap hari</string>
<string name="every_week">Setiap minggu</string>
<string name="two_times_per_week">2 kali setiap minggu</string>
<string name="five_times_per_week">5 kali setiap minggu</string>
<string name="custom_frequency">Tersuai ...</string>
<string name="help">Bantuan &amp; FAQ</string>
<string name="could_not_export">Ekspor data gagal.</string>
<string name="could_not_import">Impor data gagal.</string>
<string name="file_not_recognized">File tidak dikenali.</string>
<string name="habits_imported">Impor data berhasil.</string>
<string name="full_backup_success">Ekspor data berhasil.</string>
<string name="import_data">Impor data</string>
<string name="export_full_backup">Ekspor data</string>
<string name="import_data_summary">Mendukung ekspor data dan file dari aplikasi Tickmate, HabitBull atau Rewire. Lihat FAQ untuk informasi lebih lanjut.</string>
<string name="export_as_csv_summary">Menghasilkan lembar kerja yang dapat dibuka menggunakan aplikasi seperti Microsoft Excel atau OpenOffice Calc. File ini tidak dapat di-impor kembali.</string>
<string name="export_full_backup_summary">Menghasilkan file yang berisikan seluruh data. File ini dapat di-impor kembali.</string>
<string name="bug_report_failed">Gagal membuat laporan masalah.</string>
<string name="generate_bug_report">Membuat laporan masalah</string>
<string name="troubleshooting">Troubleshoot</string>
<string name="help_translate">Bantu terjemahkan</string>
<string name="night_mode">Mode malam</string>
<string name="use_pure_black">Gunakan warna hitam pada mode malam</string>
<string name="pure_black_description">Ganti warna latar abu-abu dengan warna hitam pada mode malam. Mengurangi penggunaan baterai pada layar AMOLED.</string>
<string name="interface_preferences">Antar muka</string>
<string name="reverse_days">Ubah urutan hari</string>
<string name="reverse_days_description">Tampilkan hari dalam urutan terbalik pada layar utama</string>
<string name="day">Hari</string>
<string name="week">Minggu</string>
<string name="month">Bulan</string>
<string name="quarter">Perempat</string>
<string name="year">Tahun</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">kali dalam</string>
<string name="every_x_days">Setiap %d hari</string>
<string name="every_x_weeks">Setiap %d minggu</string>
<string name="every_x_months">Setiap %d bulan</string>
<string name="score">Nilai</string>
<string name="reminder_sound">Suara pengingat</string>
<string name="none">Hening</string>
</resources>

View File

@@ -1,181 +1,178 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.com-->
<!--
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
~
~ This file is part of Loop Habit Tracker.
~
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by the
~ Free Software Foundation, either version 3 of the License, or (at your
~ option) any later version.
~
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">"Loop Habit Tracker"</string>
<string name="main_activity_title">"Kebiasaan"</string>
<string name="action_settings">"Pengaturan"</string>
<string name="edit">"Ubah"</string>
<string name="delete">"Hapus"</string>
<string name="archive">"Arsip"</string>
<string name="unarchive">"Keluarkan dari arsip"</string>
<string name="add_habit">"Tambah Kebiasaan"</string>
<string name="color_picker_default_title">"Ganti warna"</string>
<string name="toast_habit_created">"Kebiasaan ditambahkan."</string>
<string name="toast_habit_deleted">"Kebiasaan dihapus."</string>
<string name="toast_habit_restored">"Kebiasaan dikembalikan."</string>
<string name="toast_nothing_to_undo">"Tidak ada aksi sebelumnya."</string>
<string name="toast_nothing_to_redo">"Tidak ada aksi sesudahnya."</string>
<string name="toast_habit_changed">"Kebiasaan diubah."</string>
<!-- This appears when the user edits a habit, and then undoes the action. The habit is "changed back" to what is was before. Alternatively, "Habit restored". -->
<string name="toast_habit_changed_back">"Kebiasaan telah dikembalikan."</string>
<string name="toast_habit_archived">"Kebiasaan diarsipkan."</string>
<string name="toast_habit_unarchived">"Kebiasaan dikeluarkan dari arsip."</string>
<string name="overview">"Keseluruhan"</string>
<string name="habit_strength">"Kekuatan Kebiasaan"</string>
<string name="history">"Riwayat"</string>
<string name="clear">"Bersihkan"</string>
<string name="description_hint">"Pertanyaan (Apakah kamu ... hari ini?)"</string>
<!-- This and the next two terms form the sentence "Repeat 3 times in 7 days" that you see when you create a habit. Let me know if you have trouble adapting this into your language. -->
<string name="repeat">"Ulangi"</string>
<string name="times_every">"kali dalam"</string>
<string name="days">"hari"</string>
<string name="reminder">"Pengingat"</string>
<string name="discard">"Batalkan"</string>
<string name="save">"Simpan"</string>
<!-- Streak as in "winning streak". That is, the number of times a user has performed a habit consecutively. Similar terms are "chains" or "series". -->
<string name="streaks">"Rentetan"</string>
<string name="no_habits_found">"Anda tidak memiliki Kebiasaan yang aktif"</string>
<string name="long_press_to_toggle">"Tekan dan tahan untuk menambah atau menghapus tanda cek"</string>
<string name="reminder_off">"Mati"</string>
<string name="validation_name_should_not_be_blank">"Nama tidak boleh kosong."</string>
<string name="validation_number_should_be_positive">"Angka harus positif."</string>
<string name="validation_at_most_one_rep_per_day">"Maksimal satu kali pengulangan dalam satu hari"</string>
<string name="create_habit">"Buat Kebiasaan"</string>
<string name="edit_habit">"Ubah Kebiasaan"</string>
<string name="check">"Tanda cek"</string>
<string name="snooze">"Tunda"</string>
<string name="app_name">Loop Habit Tracker</string>
<string name="main_activity_title">Kebiasaan</string>
<string name="action_settings">Pengaturan</string>
<string name="edit">Sunting</string>
<string name="delete">Hapus</string>
<string name="archive">Arsip</string>
<string name="unarchive">Keluarkan dari arsip</string>
<string name="add_habit">Tambah Kebiasaan</string>
<string name="color_picker_default_title">Ganti warna</string>
<string name="toast_habit_created">Kebiasaan ditambahkan.</string>
<string name="toast_habit_deleted">Kebiasaan dihapus.</string>
<string name="toast_habit_restored">Kebiasaan dipulihkan</string>
<string name="toast_nothing_to_undo">Tidak ada aksi sebelumnya.</string>
<string name="toast_nothing_to_redo">Tidak ada aksi sesudahnya.</string>
<string name="toast_habit_changed">Kebiasaan diubah.</string>
<string name="toast_habit_changed_back">Kebiasaan telah dikembalikan.</string>
<string name="toast_habit_archived">Kebiasaan diarsipkan.</string>
<string name="toast_habit_unarchived">Kebiasaan dikeluarkan dari arsip.</string>
<string name="overview">Ikhtisar</string>
<string name="habit_strength">Kekuatan Kebiasaan</string>
<string name="history">Riwayat</string>
<string name="clear">Bersihkan</string>
<string name="description_hint">Pertanyaan (Sudahkah Anda ... hari ini?)</string>
<string name="repeat">Ulangi</string>
<string name="times_every">kali dalam</string>
<string name="days">hari</string>
<string name="reminder">Pengingat</string>
<string name="discard">Batalkan</string>
<string name="save">Simpan</string>
<string name="streaks">Rentetan</string>
<string name="no_habits_found">Anda tidak memiliki Kebiasaan yang aktif</string>
<string name="long_press_to_toggle">Tekan dan tahan untuk menambah atau menghapus centang</string>
<string name="reminder_off">Mati</string>
<string name="validation_name_should_not_be_blank">Nama tidak boleh kosong.</string>
<string name="validation_number_should_be_positive">Angka harus positif.</string>
<string name="validation_at_most_one_rep_per_day">Maksimal satu kali pengulangan dalam satu hari</string>
<string name="create_habit">Buat Kebiasaan</string>
<string name="edit_habit">Sunting Kebiasaan</string>
<string name="check">Tanda cek</string>
<string name="snooze">Tunda</string>
<!-- App introduction -->
<string name="intro_title_1">"Selamat datang"</string>
<string name="intro_description_1">"Loop Habit Tracker membantu mencatat dan mengelola Kebiasaan baik Anda."</string>
<string name="intro_title_2">"Buat beberapa Kebiasaan baru"</string>
<string name="intro_description_2">"Berikan tanda cek setiap kali Anda selesai melakukannya."</string>
<string name="intro_title_3">"Terus lakukan"</string>
<string name="intro_description_3">"Kebiasaan yang dilakukan secara konsisten dalam jangka waktu panjang akan mendapatkan tanda bintang penuh."</string>
<string name="intro_title_4">"Catat perkembangan Anda"</string>
<string name="intro_description_4">"Detail grafik menampilkan perkembangan Kebiasaanmu dari waktu ke waktu."</string>
<string name="interval_15_minutes">"15 menit"</string>
<string name="interval_30_minutes">"30 menit"</string>
<string name="interval_1_hour">"1 jam"</string>
<string name="interval_2_hour">"2 jam"</string>
<string name="interval_4_hour">"4 jam"</string>
<string name="interval_8_hour">"8 jam"</string>
<string name="pref_toggle_title">"Tandai dengan cepat."</string>
<string name="pref_toggle_description">"Lebih nyaman namun memungkinkan kesalahan."</string>
<string name="pref_snooze_interval_title">"Durasi tunda sejenak pada pengingat"</string>
<string name="pref_rate_this_app">"Berikan rating aplikasi ini di Google Play"</string>
<string name="pref_send_feedback">"Kirimkan umpan balik kepada Developer"</string>
<string name="pref_view_source_code">"Lihat kode aplikasi di GitHub"</string>
<string name="pref_view_app_introduction">"Perkenalan aplikasi"</string>
<string name="links">"Tautan"</string>
<string name="behavior">"Kebiasaan"</string>
<string name="name">"Nama"</string>
<string name="show_archived">"Tampilkan arsip"</string>
<string name="settings">"Pengaturan"</string>
<string name="snooze_interval">"Durasi tunda sejenak"</string>
<string name="hint_title">"Tahukah kamu?"</string>
<string name="hint_drag">"Untuk mengatur urutan, tekan dan tahan judul Kebiasaan lalu tempatkan pada posisi yang Anda inginkan."</string>
<string name="hint_landscape">"Anda dapat melihat tampilan hari dengan merubah posisi menjadi mode landscape."</string>
<string name="delete_habits">"Hapus Kebiasaan"</string>
<string name="delete_habits_message">"Kebiasaan ini akan dihapus permanen. Tidak dapat dikembalikan."</string>
<string name="weekends">"Akhir pekan"</string>
<string name="any_weekday">"Senin - Jumat"</string>
<string name="any_day">"Seluruh hari dalam satu minggu"</string>
<string name="select_weekdays">"Pilih hari"</string>
<string name="export_to_csv">"Ekspor (CSV)"</string>
<string name="done_label">"Selesai"</string>
<string name="clear_label">"Hapus"</string>
<string name="select_hours">"Pilih jam"</string>
<string name="select_minutes">"Pilih menit"</string>
<!-- Short description used on the Google Play store. There is an 80-character limit. -->
<string name="store_short_description">"Buat Kebiasaan baik dan catat perkembangannya setiap waktu (bebas iklan)"</string>
<string name="store_description_1">"Loop membantu Anda untuk membuat dan mengelola Kebiasaan baik, memungkinkan Anda mencapai gol jangka panjang. Grafik detail dan statistik menampilkan perkembangan Kebiasaan Anda dari waktu ke waktu. Aplikasi ini bebas iklan dan open-source."</string>
<string name="store_feature_interface">"&lt;b&gt;Sederhana, minimalis dengan balutan antarmuka yang modern&lt;/b&gt;
Loop memiliki tampilan minimalis yang mudah digunakan dan mengikuti panduan material design."</string>
<string name="store_feature_score">"&lt;b&gt;Skor Kebiasaan&lt;/b&gt;
Algoritma Loop akan menghitung kekuatan Kebiasaan Anda. Setiap kali Anda melakukan pengulangan maka skor Anda akan bertambah, sebaliknya jika Anda tidak mengerjakan Kebiasaan maka nilai akan dikurangi. Beberapa hari yang terlewat tidak akan menghapus seluruh perkembangan Kebiasaan Anda."</string>
<string name="store_feature_statistics">"&lt;b&gt;Detail Grafik dan Statistik&lt;/b&gt;
Lihat perkembangan Kebiasaan dari waktu ke waktu dengan tampilan yang menarik dan detail. Geser ke bawah untuk melihat seluruh catatan Kebiasaan Anda."</string>
<string name="store_feature_schedules">"&lt;b&gt;Pengaturan jadwal fleksibel&lt;/b&gt;
Mendukung kebiasaan harian dan kebiasaan dengan penjadwalan yang kompleks, seperti 3 kali dalam setiap minggu; 2 minggu sekali; hingga 2 hari sekali."</string>
<string name="store_feature_reminders">"&lt;b&gt;Pengingat&lt;/b&gt;
Anda dapat membuat pengingat untuk setiap Kebiasaan, dengan waktu yang telah Anda tentukan setiap harinya. Mudah untuk di-cek, batalkan ataupun tunda melalui panel notifikasi tanpa perlu membuka aplikasi."</string>
<string name="store_feature_opensource">"&lt;b&gt;Bebas dari iklan dan open-source&lt;/b&gt;
Tidak ada iklan, notifikasi menyebalkan dan juga hak akses yang tidak dibutuhkan.
Kode aplikasi tersedia dengan lisensi GPLv3"</string>
<string name="store_feature_wear">"&lt;b&gt;Mendukung Smartwatch&lt;/b&gt;
Anda dapat dengan mudah mengecek, menunda ataupun membatalkan pengingat melalui smartwatch Anda."</string>
<string name="about">"Tentang"</string>
<string name="translators">"Penerjemah"</string>
<string name="developers">"Developer"</string>
<!-- %s will get replaced by the version number. For example, "Versão %d" will become "Versão 1.2.0". -->
<string name="version_n">"Versi %s"</string>
<string name="frequency">"Frekuensi"</string>
<string name="checkmark">"Cek"</string>
<!-- This is a shorter version of "Habit Strength" -->
<string name="strength">"Kekuatan"</string>
<string name="best_streaks">"Rentetan terbaik"</string>
<string name="current_streaks">"Rentetan saat ini"</string>
<string name="number_of_repetitions">"Jumlah pengulangan"</string>
<string name="last_x_days">"%d hari terakhir"</string>
<string name="last_x_weeks">"%d minggu terakhir"</string>
<string name="last_x_months">"%d bulan terakhir"</string>
<string name="last_x_years">"%d tahun terakhir"</string>
<!-- "All time" number of repetitions. Or number of repetitions "since the beginning". -->
<string name="all_time">"Seluruh waktu"</string>
<string name="every_day">"Setiap hari"</string>
<string name="every_week">"Setiap minggu"</string>
<string name="two_times_per_week">"2 kali setiap minggu"</string>
<string name="five_times_per_week">"5 kali setiap minggu"</string>
<string name="custom_frequency">"Tersuai ..."</string>
<string name="help">"Bantuan &amp; FAQ"</string>
<string name="could_not_export">"Ekspor data gagal."</string>
<string name="could_not_import">"Impor data gagal."</string>
<!-- Appears when the user tries to import a file which we do not support or recognize. -->
<string name="file_not_recognized">"File tidak dikenali."</string>
<string name="habits_imported">"Impor data berhasil."</string>
<string name="full_backup_success">"Ekspor data berhasil."</string>
<string name="import_data">"Impor data"</string>
<string name="export_full_backup">"Ekspor data"</string>
<string name="import_data_summary">"Mendukung ekspor data dan file dari aplikasi Tickmate, HabitBull atau Rewire. Lihat FAQ untuk informasi lebih lanjut."</string>
<string name="export_as_csv_summary">"Menghasilkan lembar kerja yang dapat dibuka menggunakan aplikasi seperti Microsoft Excel atau OpenOffice Calc. File ini tidak dapat di-impor kembali."</string>
<string name="export_full_backup_summary">"Menghasilkan file yang berisikan seluruh data. File ini dapat di-impor kembali."</string>
<string name="bug_report_failed">"Gagal membuat laporan masalah."</string>
<string name="generate_bug_report">"Membuat laporan masalah"</string>
<string name="troubleshooting">"Troubleshoot"</string>
<string name="help_translate">"Bantu terjemahkan"</string>
<string name="night_mode">"Mode malam"</string>
<string name="use_pure_black">"Gunakan warna hitam pada mode malam"</string>
<string name="pure_black_description">"Ganti warna latar abu-abu dengan warna hitam pada mode malam. Mengurangi penggunaan baterai pada layar AMOLED."</string>
<string name="interface_preferences">"Antar muka"</string>
<string name="reverse_days">"Ubah urutan hari"</string>
<string name="reverse_days_description">"Tampilkan hari dalam urutan terbalik pada layar utama"</string>
<string name="day">"Hari"</string>
<string name="week">"Minggu"</string>
<string name="month">"Bulan"</string>
<!-- Three-month period -->
<string name="quarter">"Perempat"</string>
<string name="year">"Tahun"</string>
<string name="intro_title_1">Selamat datang</string>
<string name="intro_description_1">Loop Habit Tracker membantu mencatat dan mengelola Kebiasaan baik Anda.</string>
<string name="intro_title_2">Buat beberapa Kebiasaan baru</string>
<string name="intro_description_2">Berikan tanda cek setiap kali Anda selesai melakukannya.</string>
<string name="intro_title_3">Terus lakukan</string>
<string name="intro_description_3">Kebiasaan yang dilakukan secara konsisten dalam jangka waktu panjang akan mendapatkan tanda bintang penuh.</string>
<string name="intro_title_4">Lacak perkembangan Anda</string>
<string name="intro_description_4">Grafik terperinci menampilkan perkembangan Kebiasaanmu dari waktu ke waktu.</string>
<string name="interval_15_minutes">15 menit</string>
<string name="interval_30_minutes">30 menit</string>
<string name="interval_1_hour">1 jam</string>
<string name="interval_2_hour">2 jam</string>
<string name="interval_4_hour">4 jam</string>
<string name="interval_8_hour">8 jam</string>
<string name="interval_24_hour">24 jam</string>
<string name="pref_toggle_title">Tandai dengan cepat.</string>
<string name="pref_toggle_description">Beri tanda cek dengan sekali ketuk bukan tekan-dan-tahan. Lebih nyaman namun memungkinkan kesalahan.</string>
<string name="pref_snooze_interval_title">Durasi tunda sejenak pada pengingat</string>
<string name="pref_rate_this_app">Berikan rating aplikasi ini di Google Play</string>
<string name="pref_send_feedback">Kirimkan umpan balik kepada Developer</string>
<string name="pref_view_source_code">Lihat kode aplikasi di GitHub</string>
<string name="pref_view_app_introduction">Tampilkan perkenalan aplikasi</string>
<string name="links">Tautan</string>
<string name="behavior">Kebiasaan</string>
<string name="name">Nama</string>
<string name="settings">Pengaturan</string>
<string name="snooze_interval">Durasi tunda sejenak</string>
<string name="hint_title">Sudahkah Anda tahu?</string>
<string name="hint_drag">Untuk mengatur urutan, tekan dan tahan judul Kebiasaan lalu tempatkan pada posisi yang Anda inginkan.</string>
<string name="hint_landscape">Anda dapat melihat tampilan hari dengan merubah posisi menjadi mode landscape.</string>
<string name="delete_habits">Hapus Kebiasaan</string>
<string name="delete_habits_message">Kebiasaan ini akan dihapus secara permanen. Tindakan ini tidak dapat dibatalkan.</string>
<string name="habit_not_found">Kebiasaan telah dihapus / tidak ditemukan</string>
<string name="weekends">Akhir pekan</string>
<string name="any_weekday">Senin - Jumat</string>
<string name="any_day">Seluruh hari dalam satu minggu</string>
<string name="select_weekdays">Pilih hari</string>
<string name="export_to_csv">Ekspor (CSV)</string>
<string name="done_label">Selesai</string>
<string name="clear_label">Hapus</string>
<string name="select_hours">Pilih jam</string>
<string name="select_minutes">Pilih menit</string>
<string name="about">Tentang</string>
<string name="translators">Penerjemah</string>
<string name="developers">Developer</string>
<string name="version_n">Versi %s</string>
<string name="frequency">Frekuensi</string>
<string name="checkmark">Cek</string>
<string name="strength">Kekuatan</string>
<string name="best_streaks">Rentetan terbaik</string>
<string name="current_streaks">Rentetan saat ini</string>
<string name="number_of_repetitions">Jumlah pengulangan</string>
<string name="last_x_days">%d hari terakhir</string>
<string name="last_x_weeks">%d minggu terakhir</string>
<string name="last_x_months">%d bulan terakhir</string>
<string name="last_x_years">%d tahun terakhir</string>
<string name="all_time">Seluruh waktu</string>
<string name="every_day">Setiap hari</string>
<string name="every_week">Setiap minggu</string>
<string name="two_times_per_week">2 kali per minggu</string>
<string name="five_times_per_week">5 kali per minggu</string>
<string name="custom_frequency">Sesuaikan &#8230;</string>
<string name="help">Bantuan &amp; FAQ</string>
<string name="could_not_export">Gagal mengekspor data.</string>
<string name="could_not_import">Gagal mengimpor data.</string>
<string name="file_not_recognized">File tidak dikenali.</string>
<string name="habits_imported">Impor data berhasil.</string>
<string name="full_backup_success">Seluruh data berhasil di-ekpor.</string>
<string name="import_data">Impor data</string>
<string name="export_full_backup">Ekspor keseluruhan data</string>
<string name="import_data_summary">Mendukung ekspor data dan berkas dari aplikasi Tickmate, HabitBull atau Rewire. Lihat FAQ untuk informasi lebih lanjut.</string>
<string name="export_as_csv_summary">Menghasilkan lembar kerja yang dapat dibuka menggunakan aplikasi seperti Microsoft Excel atau OpenOffice Calc. Berkas ini tidak dapat di-impor kembali.</string>
<string name="export_full_backup_summary">Menghasilkan berkas yang berisikan seluruh data. Berkas ini dapat di-impor kembali.</string>
<string name="bug_report_failed">Gagal membuat laporan masalah.</string>
<string name="generate_bug_report">Membuat laporan masalah</string>
<string name="troubleshooting">Penyelesaian masalah</string>
<string name="help_translate">Bantu menerjemahkan aplikasi ini</string>
<string name="night_mode">Mode malam</string>
<string name="use_pure_black">Gunakan warna hitam pada mode malam</string>
<string name="pure_black_description">Ganti warna latar abu-abu dengan warna hitam pada mode malam. Mengurangi penggunaan baterai pada layar AMOLED.</string>
<string name="interface_preferences">Antar muka</string>
<string name="reverse_days">Ubah urutan hari</string>
<string name="reverse_days_description">Tampilkan hari dalam urutan terbalik pada layar utama</string>
<string name="day">Hari</string>
<string name="week">Minggu</string>
<string name="month">Bulan</string>
<string name="quarter">Kuartal</string>
<string name="year">Tahun</string>
<string name="total">Total</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">"kali dalam"</string>
<string name="every_x_days">"Setiap %d hari"</string>
<string name="every_x_weeks">"Setiap %d minggu"</string>
<string name="every_x_months">"Setiap %d bulan"</string>
<!-- The old "habit strength" has been replaced by "score". Feel free to translate "score" as "strength" or "stability" if it sounds more natural in your language. -->
<string name="score">"Nilai"</string>
<string name="reminder_sound">"Suara pengingat"</string>
<!-- Appears when the user disables the reminder sound. Could also be "no sound", "mute" or "silent". -->
<string name="none">"Hening"</string>
<string name="time_every">kali dalam</string>
<string name="every_x_days">Setiap %d hari</string>
<string name="every_x_weeks">Setiap %d minggu</string>
<string name="every_x_months">Setiap %d bulan</string>
<string name="score">Skor</string>
<string name="reminder_sound">Suara pengingat</string>
<string name="none">Hening</string>
<string name="filter">Saring</string>
<string name="hide_completed">Sembunyikan yang selesai</string>
<string name="hide_archived">Sembunyikan arsip</string>
<string name="sticky_notifications">Jadikan notifikasi lengket</string>
<string name="sticky_notifications_description">Cegah pemberitahuan dari sapuan.</string>
<string name="repair_database">Perbaiki Basis Data</string>
<string name="database_repaired">Basis Data diperbaiki.</string>
<string name="uncheck">Hapus centang</string>
<string name="toggle">Alih</string>
<string name="action">Tindakan</string>
<string name="habit">Kebiasaan</string>
<string name="sort">Urutkan</string>
<string name="manually">Secara manual</string>
<string name="by_name">Berdasarkan nama</string>
<string name="by_color">Berdasarkan Warna</string>
<string name="by_score">Berdasarkan Skor</string>
<string name="download">Unduh</string>
<string name="export">Ekspor</string>
</resources>

View File

@@ -26,17 +26,17 @@
<string name="delete">Elimina</string>
<string name="archive">Archivia</string>
<string name="unarchive">Ripristina</string>
<string name="add_habit">Aggiungi</string>
<string name="add_habit">Aggiungi abitudine</string>
<string name="color_picker_default_title">Cambia colore</string>
<string name="toast_habit_created">Abitudine creata.</string>
<string name="toast_habit_deleted">Abitudine rimossa.</string>
<string name="toast_habit_restored">Abitudine ripristinata.</string>
<string name="toast_nothing_to_undo">Niente da annullare.</string>
<string name="toast_nothing_to_redo">Niente da ripetere.</string>
<string name="toast_habit_changed">Abitudine modificata.</string>
<string name="toast_habit_changed_back">Abitudine ripristinata.</string>
<string name="toast_habit_archived">Abitudine archiviata.</string>
<string name="toast_habit_unarchived">Abitudine ripristinata.</string>
<string name="toast_habit_created">Abitudine creata</string>
<string name="toast_habit_deleted">Abitudine rimossa</string>
<string name="toast_habit_restored">Abitudine ripristinata</string>
<string name="toast_nothing_to_undo">Niente da annullare</string>
<string name="toast_nothing_to_redo">Niente da ripetere</string>
<string name="toast_habit_changed">Abitudine modificata</string>
<string name="toast_habit_changed_back">Abitudine ripristinata</string>
<string name="toast_habit_archived">Abitudine archiviata</string>
<string name="toast_habit_unarchived">Abitudine ripristinata</string>
<string name="overview">Panoramica</string>
<string name="habit_strength">Forza dell\'abitudine</string>
<string name="history">Cronologia</string>
@@ -65,7 +65,7 @@
<string name="intro_title_2">Aggiungi qualche nuova abitudine</string>
<string name="intro_description_2">Ogni giorno, dopo aver portato a termine la tua abitudine, spuntala nell\'app.</string>
<string name="intro_title_3">Continua così</string>
<string name="intro_description_3">Abitudini portate a termine con regolarità per un lungo periodo ti faranno guadagnare una stella intera.</string>
<string name="intro_description_3">Le abitudini portate a termine regoalrmente per un lungo periodo riceveranno una stella piena.</string>
<string name="intro_title_4">Segui i tuoi progressi</string>
<string name="intro_description_4">Grafici dettagliati ti mostrano come le tue abitudini sono migliorate nel corso del tempo.</string>
<string name="interval_15_minutes">15 minuti</string>
@@ -76,7 +76,7 @@
<string name="interval_8_hour">8 ore</string>
<string name="interval_24_hour">24 ore</string>
<string name="pref_toggle_title">Spunta le ripetizioni velocemente</string>
<string name="pref_toggle_description">Più comodo, ma potrebbe causare delle spunte accidentali.</string>
<string name="pref_toggle_description">Metti le spunte con un tocco singolo invece che tenendo premuto. Più comodo, ma potrebbe causare delle spunte accidentali.</string>
<string name="pref_snooze_interval_title">Intervallo di ritardo dei promemoria</string>
<string name="pref_rate_this_app">Valuta quest\'app su Google Play</string>
<string name="pref_send_feedback">Manda un feedback allo sviluppatore</string>
@@ -88,7 +88,7 @@
<string name="settings">Impostazioni</string>
<string name="snooze_interval">Snooze</string>
<string name="hint_title">Lo sapevi?</string>
<string name="hint_drag">Per riordinare la lista, premi e mantieni premuta l\'abitudine e spostala nella posizione desiderata.</string>
<string name="hint_drag">Per riordinare le voci, tieni premuto sul nome dell\'abitudine, poi spostala nella posizione corretta.</string>
<string name="hint_landscape">Puoi vedere più giorni mettendo il tuo telefono orizzontale.</string>
<string name="delete_habits">Elimina abitudine</string>
<string name="delete_habits_message">L\'abitudine verrà cancellata definitivamente. Non sarà possibile annullare.</string>
@@ -158,8 +158,8 @@
<string name="reminder_sound">Suono notifica</string>
<string name="none">Nessuno</string>
<string name="filter">Filtra</string>
<string name="hide_completed">Nascosti</string>
<string name="hide_archived">Nascosti</string>
<string name="hide_completed">Nascondi completati</string>
<string name="hide_archived">Nascondi archiviati</string>
<string name="sticky_notifications">Notifiche non rimuovibili</string>
<string name="sticky_notifications_description">Impedisce di poter rimuovere le notifiche.</string>
<string name="repair_database">Ripara database</string>

View File

@@ -74,6 +74,7 @@
<string name="interval_2_hour">2 時間</string>
<string name="interval_4_hour">4 時間</string>
<string name="interval_8_hour">8 時間</string>
<string name="interval_24_hour">24時間</string>
<string name="pref_toggle_title">クリックで繰り返しを切り替え</string>
<string name="pref_toggle_description">便利になりますが、間違って切り替えが起こる可能性があります。</string>
<string name="pref_snooze_interval_title">リマインダーのスヌーズ間隔</string>
@@ -146,6 +147,7 @@
<string name="month"></string>
<string name="quarter">四半期</string>
<string name="year"></string>
<string name="total">合計</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">回 /</string>
<string name="every_x_days">%d 日ごと</string>
@@ -154,4 +156,7 @@
<string name="score">スコア</string>
<string name="reminder_sound">リマインダー サウンド</string>
<string name="none">なし</string>
<string name="filter">フィルター</string>
<string name="download">ダウンロード</string>
<string name="export">エクスポート</string>
</resources>

View File

@@ -25,21 +25,21 @@
<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_habit_restored">습관 복원니다.</string>
<string name="toast_nothing_to_undo">복원할 것이 없습니다.</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_habit_restored">습관 복원되었습니다.</string>
<string name="toast_nothing_to_undo">취소할 것이 없습니다.</string>
<string name="toast_nothing_to_redo">복원할 것이 없습니다.</string>
<string name="toast_habit_changed">습관 수정습니다.</string>
<string name="toast_habit_changed_back">습관 복원습니다.</string>
<string name="toast_habit_archived">습관 보관니다.</string>
<string name="toast_habit_unarchived">습관을 제거합니다.</string>
<string name="overview"></string>
<string name="habit_strength">습관 완성정</string>
<string name="history">기록</string>
<string name="toast_habit_changed">습관 수정되었습니다.</string>
<string name="toast_habit_changed_back">습관 복원되었습니다.</string>
<string name="toast_habit_archived">습관 보관되었습니다.</string>
<string name="toast_habit_unarchived">습관 보관이 해제되었습니다.</string>
<string name="overview">개요</string>
<string name="habit_strength">습관 </string>
<string name="history">이력</string>
<string name="clear">지우기</string>
<string name="description_hint">질문 (오늘 ... 했나요?)</string>
<string name="repeat">반복</string>
@@ -48,66 +48,68 @@
<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="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">숫자는 0보다 커야합니다.</string>
<string name="validation_at_most_one_rep_per_day">하루에 한 번 반복 가능합니다.</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="check">완료</string>
<string name="snooze">나중에</string>
<!-- App introduction -->
<string name="intro_title_1">안녕하세요</string>
<string name="intro_title_1">환영합니다</string>
<string name="intro_description_1">Loop은 당신이 좋은 습관을 만들고 유지하도록 도와줍니다.</string>
<string name="intro_title_2">새로운 습관을 만들어 보세요.</string>
<string name="intro_description_2">매일매일, 습관을 수행한 뒤에, 앱에 기록하세요.</string>
<string name="intro_description_2">매일 습관을 수행하고 앱에 기록하세요.</string>
<string name="intro_title_3">계속 반복하세요</string>
<string name="intro_description_3">일정 시간동안 유지 습관은 참잘했어요도장을 얻습니다.</string>
<string name="intro_description_3">오랫동안 지속적으로 유지 습관은 별표를 얻습니다.</string>
<string name="intro_title_4">습관을 관리하세요</string>
<string name="intro_description_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="interval_24_hour">24시간</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">Google Play에서 평가</string>
<string name="pref_send_feedback">발자에게 피드백</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="settings">설정</string>
<string name="snooze_interval">미루기</string>
<string name="hint_title">알고 계시나요?</string>
<string name="hint_drag">습관 순서를 조정하려면, 습관을 길게 눌러 끌어당겨서 다른 위치로 옮길 수 있습니다.</string>
<string name="hint_landscape">화면을 눕혀서 보면 더 많은 날들을 볼 수 있니다.</string>
<string name="snooze_interval">스누즈 간격</string>
<string name="hint_title">시나요?</string>
<string name="hint_drag">목록의 순서를 재배치하려면, 습관의 제목을 길게 누르고 다른 위치로 드래그하면 됩니다.</string>
<string name="hint_landscape">가로 모드에서는 더 많은 날짜를 볼 수 있니다.</string>
<string name="delete_habits">습관 삭제</string>
<string name="delete_habits_message">습관을 삭제합니다. 삭제하면 다시 복원할 수 없습니다.</string>
<string name="delete_habits_message">습관을 영구적으로 삭제합니다. 이 작업은 취소할 수 없습니다.</string>
<string name="habit_not_found">습관 삭제 / 찾을 수 없음</string>
<string name="weekends">주말</string>
<string name="any_weekday">주중</string>
<string name="any_day">매일</string>
<string name="select_weekdays">일 선택</string>
<string name="export_to_csv">정보 내보내기 (CSV)</string>
<string name="select_weekdays">일 선택</string>
<string name="export_to_csv">CSV로 내보내기</string>
<string name="done_label">완료</string>
<string name="clear_label">지우기</string>
<string name="select_hours">시간 선택</string>
<string name="select_minutes">분 선택</string>
<string name="about">정보</string>
<string name="translators">번역</string>
<string name="developers">발자</string>
<string name="translators">번역</string>
<string name="developers">발자</string>
<string name="version_n">버전 %s</string>
<string name="frequency">반복수</string>
<string name="checkmark">습관기록</string>
<string name="strength">습관 완성정</string>
<string name="best_streaks">가장 길게 이은 기록</string>
<string name="frequency">빈도</string>
<string name="checkmark">체크</string>
<string name="strength"></string>
<string name="best_streaks">최고 연속 기록</string>
<string name="current_streaks">현재 기록</string>
<string name="number_of_repetitions">반복한 횟수</string>
<string name="last_x_days">이전 %d일 동안</string>
@@ -121,37 +123,56 @@
<string name="five_times_per_week">일주일에 5번</string>
<string name="custom_frequency">직접 정하기</string>
<string name="help">도움 &amp; FAQ</string>
<string name="could_not_export">내보내기 오류.</string>
<string name="could_not_import">가져오기 오류.</string>
<string name="file_not_recognized">지원하지 않는 파일입니다.</string>
<string name="habits_imported">가져오기 완료.</string>
<string name="full_backup_success">백업 완료.</string>
<string name="could_not_export">데이터 내보내기에 실패했습니다.</string>
<string name="could_not_import">데이터 가져오기에 실패했습니다.</string>
<string name="file_not_recognized">파일을 인식할 수 없습니다.</string>
<string name="habits_imported">습관 가져오기에 성공했습니다.</string>
<string name="full_backup_success">전체 백업 내보내기에 성공했습니다.</string>
<string name="import_data">데이터 가져오기</string>
<string name="export_full_backup">백업 내보내기</string>
<string name="import_data_summary">Tickmate, HabitBull, Rewire의 백업파일도 지원합니다. 더 자세한 설명은 FAQ에 있습니다.</string>
<string name="export_as_csv_summary">Microsoft Excel 혹은, OpenOffice Calc 같은 스프레드시트 프로그램으로 열 수 있는 CSV파일로 내보냅니다. 다만, CSV파일 다시 가져올 수 없습니다.</string>
<string name="export_full_backup_summary">모든 데이터를 백업합니다. 나중에 이 파일 가져올 수 있습니다.</string>
<string name="bug_report_failed">오류보고서 작성하지 못했습니다.</string>
<string name="import_data_summary">Tickmate, HabitBull, Rewire에서 생성된 파일도 지원합니다. 더 자세한 설명은 FAQ에 있습니다.</string>
<string name="export_as_csv_summary">Microsoft Excel OpenOffice Calc 같은 스프레드시트 소프트웨어로 열 수 있는 파일을 생성합니다. 이 파일 다시 가져올 수 없습니다.</string>
<string name="export_full_backup_summary">모든 데이터를 포함한 파일을 생성합니다. 이 파일은 다시 가져올 수 있습니다.</string>
<string name="bug_report_failed">오류보고서 작성에 실패했습니다.</string>
<string name="generate_bug_report">오류보고서 작성하기</string>
<string name="troubleshooting">문제가 있나요?</string>
<string name="help_translate">역을 도와주세요</string>
<string name="night_mode">어두운 배경</string>
<string name="use_pure_black">검정색 배경</string>
<string name="pure_black_description">야간모드의 회색 바탕을 검정 바탕색으로 바꿉니다. AMOLED를 사용하는 기기는 검정바탕으로 배터리를 절약할 수 있습니다.</string>
<string name="interface_preferences">바탕화면 설정</string>
<string name="troubleshooting">문제 해결</string>
<string name="help_translate">이 앱의 번역을 도와주세요</string>
<string name="night_mode">야간 모드</string>
<string name="use_pure_black">야간 모드에서 검정색 사용하기</string>
<string name="pure_black_description">야간 모드의 회색 배경을 검정색으로 대체합니다. AMOLED 디스플레이를 사용하는 기기에서 배터리 사용량을 감소시킵니다.</string>
<string name="interface_preferences">인터페이스</string>
<string name="reverse_days">날짜 순서 뒤집기</string>
<string name="reverse_days_description">날짜 순서를 뒤바꾸어 보여줍니다.</string>
<string name="reverse_days_description">메인 화면의 날짜를 역순으로 보여줍니다.</string>
<string name="day"></string>
<string name="week"></string>
<string name="month"></string>
<string name="quarter">4분기-1년</string>
<string name="quarter">분기</string>
<string name="year"></string>
<string name="total">전체</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every"></string>
<string name="every_x_days">%d일 마다</string>
<string name="every_x_weeks">%d주 마다</string>
<string name="every_x_months">%d개월 마다</string>
<string name="score">습관점수</string>
<string name="reminder_sound">알림 소리</string>
<string name="score">점수</string>
<string name="reminder_sound">알림</string>
<string name="none">무음</string>
<string name="filter">필터</string>
<string name="hide_completed">완료된 항목 숨기기</string>
<string name="hide_archived">보관된 항목 숨기기</string>
<string name="sticky_notifications">알림 고정하기</string>
<string name="sticky_notifications_description">알림을 스와이프해서 제거하는 것을 방지합니다.</string>
<string name="repair_database">데이터베이스 복구</string>
<string name="database_repaired">데이터베이스가 복구되었습니다.</string>
<string name="uncheck">선택 해제</string>
<string name="toggle">전환</string>
<string name="action">동작</string>
<string name="habit">습관</string>
<string name="sort">정렬</string>
<string name="manually">수동으로</string>
<string name="by_name">이름 순으로</string>
<string name="by_color">색상 순으로</string>
<string name="by_score">점수 순으로</string>
<string name="download">다운로드</string>
<string name="export">내보내기</string>
</resources>

View File

@@ -26,29 +26,28 @@
<string name="delete">Verwijder</string>
<string name="archive">Archiveer</string>
<string name="unarchive">Dearchiveren</string>
<string name="add_habit">Voeg gewoonte toe</string>
<string name="add_habit">Nieuwe gewoonte</string>
<string name="color_picker_default_title">Verander kleur</string>
<string name="toast_habit_created">Gewoonte aangemaakt.</string>
<string name="toast_habit_deleted">Gewoonte verwijderd.</string>
<string name="toast_habit_restored">Gewoonte hersteld.</string>
<string name="toast_habit_deleted">Gewoontes verwijderd</string>
<string name="toast_habit_restored">Gewoontes hersteld</string>
<string name="toast_nothing_to_undo">Niets om ongedaan te maken.</string>
<string name="toast_nothing_to_redo">Niets om over te doen.</string>
<string name="toast_habit_changed">Gewoonte gewijzigd.</string>
<string name="toast_habit_changed_back">Wijziging gewoonte ongedaan gemaakt.</string>
<string name="toast_habit_archived">Gewoonte gearchiveerd.</string>
<string name="toast_habit_unarchived">Gewoonte hersteld uit archief.</string>
<string name="toast_habit_archived">Gewoontes gearchiveerd</string>
<string name="toast_habit_unarchived">Gewoontes hersteld uit archief</string>
<string name="overview">Overzicht</string>
<string name="habit_strength">Gewoonte-sterkte</string>
<string name="history">Geschiedenis</string>
<string name="clear">Wis</string>
<string name="description_hint">Vraag (Heb jij vandaag .... ?)</string>
<string name="description_hint">Vraag (Heb je vandaag .... ?)</string>
<string name="repeat">Herhaal</string>
<string name="times_every">keer in </string>
<string name="days">dagen</string>
<string name="reminder">Herinnering</string>
<string name="discard">Verwijder</string>
<string name="save">\"Opslaan
\"</string>
<string name="save">Opslaan</string>
<string name="streaks">Reeksen</string>
<string name="no_habits_found">Je hebt geen actieve gewoontes</string>
<string name="long_press_to_toggle">Houdt ingedrukt om te selecteren of deselecteren</string>
@@ -77,7 +76,7 @@
<string name="interval_8_hour">8 uur</string>
<string name="interval_24_hour">24 uur</string>
<string name="pref_toggle_title">Wijzig herhalingen door kort indrukken</string>
<string name="pref_toggle_description">Handiger, maar kan onbedoelde wijzigingen veroorzaken.</string>
<string name="pref_toggle_description">Zet vinkjes met een enkel tikje in plaats van ingedrukt houden. Handiger, maar kan onbedoelde wijzigingen veroorzaken.</string>
<string name="pref_snooze_interval_title">Snooze interval voor herinneringen</string>
<string name="pref_rate_this_app">Beoordeel deze app in Google Play</string>
<string name="pref_send_feedback">Stuur feedback aan de ontwikkelaar</string>
@@ -87,14 +86,14 @@
<string name="behavior">Gedrag</string>
<string name="name">Naam</string>
<string name="settings">Instellingen</string>
<string name="snooze_interval">Later interval</string>
<string name="snooze_interval">Snooze interval</string>
<string name="hint_title">Wist je dat?</string>
<string name="hint_drag">Om de rijen te ordenen houdt de gewoonte ingedrukt en sleep het naar de gewenste plek.</string>
<string name="hint_landscape">Je kunt meer dagen zien door de telefoon in landschapmodus te zetten.</string>
<string name="hint_drag">Om de rijen te ordenen, houdt de gewoonte ingedrukt en sleep het naar de gewenste plek.</string>
<string name="hint_landscape">Je kunt meer dagen zien door de telefoon in horizontale modus te zetten.</string>
<string name="delete_habits">Verwijder gewoontes</string>
<string name="delete_habits_message">De gewoontes zullen permanent verwijderd worden. Deze actie kan niet ongedaan gemaakt worden.</string>
<string name="habit_not_found">Gewoonte verwijderd / niet gevonden</string>
<string name="weekends">In het weekend</string>
<string name="weekends">Weekends</string>
<string name="any_weekday">Maandag tot vrijdag</string>
<string name="any_day">Elke dag van de week</string>
<string name="select_weekdays">Selecteer dagen</string>
@@ -109,7 +108,7 @@
<string name="version_n">Versie %s</string>
<string name="frequency">Frequentie</string>
<string name="checkmark">Vinkje</string>
<string name="strength">Sterkte</string>
<string name="strength">Kracht</string>
<string name="best_streaks">Beste reeksen</string>
<string name="current_streaks">Huidige reeks</string>
<string name="number_of_repetitions">Aantal herhalingen</string>
@@ -122,7 +121,7 @@
<string name="every_week">Iedere week</string>
<string name="two_times_per_week">2 keer per week</string>
<string name="five_times_per_week">5 keer per week</string>
<string name="custom_frequency">Aangepast</string>
<string name="custom_frequency">Aangepast &#8230;</string>
<string name="help">Hulp en veelgestelde vragen</string>
<string name="could_not_export">Het exporteren van de data is mislukt.</string>
<string name="could_not_import">Importeren van data mislukt.</string>
@@ -141,7 +140,7 @@
<string name="night_mode">Nachtmodus</string>
<string name="use_pure_black">Gebruik puur zwart in nachtmodus</string>
<string name="pure_black_description">Vervangt grijze achtergronden door puur zwart in nachtmodus. Vermindert batterijgebruik in telefoons met AMOLED scherm.</string>
<string name="interface_preferences">Uiterlijk</string>
<string name="interface_preferences">Interface</string>
<string name="reverse_days">Omgekeerde volgorde van dagen</string>
<string name="reverse_days_description">Toon dagen in omgekeerde volgorde op het hoofdscherm</string>
<string name="day">Dag</string>

View File

@@ -19,6 +19,160 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">Loop Vaneoversikt</string>
<string name="main_activity_title">Vaner</string>
<string name="action_settings">Innstillinger</string>
<string name="edit">Redigér</string>
<string name="delete">Slett</string>
<string name="archive">Arkivér</string>
<string name="unarchive">Uarkivér</string>
<string name="add_habit">Legg til vane</string>
<string name="color_picker_default_title">Endr farge</string>
<string name="toast_habit_created">Vane laget</string>
<string name="toast_habit_deleted">Vaner slettet</string>
<string name="toast_habit_restored">Vaner gjenopprettet</string>
<string name="toast_nothing_to_undo">Ingenting å angre</string>
<string name="toast_nothing_to_redo">Ingenting å ta tilbake</string>
<string name="toast_habit_changed">Vane endret</string>
<string name="toast_habit_changed_back">Vane tilbakeendret</string>
<string name="toast_habit_archived">Vaner arkivert</string>
<string name="toast_habit_unarchived">Vaner uarkivert</string>
<string name="overview">Oversikt</string>
<string name="habit_strength">Vanestyrke</string>
<string name="history">Logg</string>
<string name="clear">Fjern</string>
<string name="description_hint">Spørsmål (Gjorde du &#8230; i dag?)</string>
<string name="repeat">Gjenta</string>
<string name="times_every">ganger på</string>
<string name="days">dager</string>
<string name="reminder">Påminnelse</string>
<string name="discard">Forkast</string>
<string name="save">Lagr</string>
<string name="streaks">Gjentakelser</string>
<string name="no_habits_found">Du har ingen aktive vaner</string>
<string name="long_press_to_toggle">Trykk og hold for å sjekke eller usjekke</string>
<string name="reminder_off">Av</string>
<string name="validation_name_should_not_be_blank">Navnet kan ikke stå tomt.</string>
<string name="validation_number_should_be_positive">Nummeret må være positivt.</string>
<string name="validation_at_most_one_rep_per_day">Du kan maks ha én repetisjon om dagen</string>
<string name="create_habit">Lag vane</string>
<string name="edit_habit">Redigér vane</string>
<string name="check">Sjekk</string>
<string name="snooze">Senere</string>
<!-- App introduction -->
<string name="intro_title_1">Velkommen</string>
<string name="intro_description_1">Loop Vaneoversikt hjelper deg med å lage og beholde gode vaner.</string>
<string name="intro_title_2">Lag noen nye vaner</string>
<string name="intro_description_2">Hver dag, etter du utfører vanen din, sett en hake på appen.</string>
<string name="intro_title_3">Fortsett med å gjøre det</string>
<string name="intro_description_3">Vaner utført konsekvent over lang tid vil få en hel stjerne.</string>
<string name="intro_title_4">Hold oversikt over fremgangen din</string>
<string name="intro_description_4">Detaljerte grafer viser deg hvordan vanene dine forbedrer seg over tid.</string>
<string name="interval_15_minutes">15 minutter</string>
<string name="interval_30_minutes">30 minutter</string>
<string name="interval_1_hour">1 time</string>
<string name="interval_2_hour">2 timer</string>
<string name="interval_4_hour">4 timer</string>
<string name="interval_8_hour">8 timer</string>
<string name="interval_24_hour">1 døgn</string>
<string name="pref_toggle_title">Veksl med enkelttrykk</string>
<string name="pref_toggle_description">Sett på haker med et enkelttrykk i stedet for å tykke og holde. Mer praktisk, men kan forårsake utilsiktede vekslinger.</string>
<string name="pref_snooze_interval_title">Slumreintervall på påminnelser</string>
<string name="pref_rate_this_app">Vurdér denne appen på Google Play</string>
<string name="pref_send_feedback">Send tilbakemelding til utviklerne</string>
<string name="pref_view_source_code">Vis kildekode på GitHub</string>
<string name="pref_view_app_introduction">Se på appintroduksjon</string>
<string name="links">Lenker</string>
<string name="behavior">Oppførsel</string>
<string name="name">Navn</string>
<string name="settings">Innstillinger</string>
<string name="snooze_interval">Slumreintervall</string>
<string name="hint_title">Visste du at?</string>
<string name="hint_drag">For å sortere innleggene, trykk og hold på navnet til vanen, deretter dra den til det korrekte stedet.</string>
<string name="hint_landscape">Du kan se flere dager ved å sette telefonen din i landskapsmodus.</string>
<string name="delete_habits">Slett vaner</string>
<string name="delete_habits_message">Vanene vil bli slettet permanent. Denne handlingen kan ikke angres.</string>
<string name="habit_not_found">Vane slettet / ikke funnet</string>
<string name="weekends">Helger</string>
<string name="any_weekday">Hverdager</string>
<string name="any_day">Hvilken som helst dag i uken</string>
<string name="select_weekdays">Velg dager</string>
<string name="export_to_csv">Eksportér som CSV</string>
<string name="done_label">Ferdig</string>
<string name="clear_label">Fjern</string>
<string name="select_hours">Velg timer</string>
<string name="select_minutes">Velg minutter</string>
<string name="about">Om</string>
<string name="translators">Translatører</string>
<string name="developers">Utviklere</string>
<string name="version_n">Versjon %s</string>
<string name="frequency">Hyppighet</string>
<string name="checkmark">Hake</string>
<string name="strength">Styrke</string>
<string name="best_streaks">Beste gjentakelser</string>
<string name="current_streaks">Nåværende gjentakelse</string>
<string name="number_of_repetitions">Repetisjonsantall</string>
<string name="last_x_days">Siste %d dager</string>
<string name="last_x_weeks">Siste %d uker</string>
<string name="last_x_months">Siste %d måneder</string>
<string name="last_x_years">Siste %d år</string>
<string name="all_time">Alltid</string>
<string name="every_day">Hver dag</string>
<string name="every_week">Hver uke</string>
<string name="two_times_per_week">2 ganger i uken</string>
<string name="five_times_per_week">5 ganger i uken</string>
<string name="custom_frequency">Tilpasset &#8230;</string>
<string name="help">Hjelp og FAQ</string>
<string name="could_not_export">Mislyktes i å eksportere data.</string>
<string name="could_not_import">Mislyktes i å importere data.</string>
<string name="file_not_recognized">Fil ikke gjenkjent.</string>
<string name="habits_imported">Vaner suksessfullt importert.</string>
<string name="full_backup_success">Hel sikkerhetskopi suksessfullt eksportert.</string>
<string name="import_data">Importér data</string>
<string name="export_full_backup">Eksportér hel sikkerhetskopi</string>
<string name="import_data_summary">Støtter hele sikkerhetskopier eksportert av denne appen, i tillegg til filer generert av Tickmate, HabitBull, og Rewire. Sjekk ut FAQ for mer informasjon.</string>
<string name="export_as_csv_summary">Genererer filer som kan bli åpnet av regnearkprogrammer som Microsoft Excel og OpenOffice Calc. Disse filene kan ikke importeres tilbake.</string>
<string name="export_full_backup_summary">Genererer en fil som inneholder all dataen din. Denne filen kan ikke importeres tilbake.</string>
<string name="bug_report_failed">Mislyktes i å generere feilrapport.</string>
<string name="generate_bug_report">Generér feilrapport</string>
<string name="troubleshooting">Feilsøkning</string>
<string name="help_translate">Hjelp med å oversette denne appen</string>
<string name="night_mode">Nattmodus</string>
<string name="use_pure_black">Bruk batterisparing i nattmodus</string>
<string name="pure_black_description">Bytter ut grå bakgunner med helt svarte i nattmodus. Reduserer betteribruk hos telefoner med AMOLED-skjerm.</string>
<string name="interface_preferences">Grensesnitt</string>
<string name="reverse_days">Omvendt dagsrekkefølge</string>
<string name="reverse_days_description">Vis dager i omvendt rekkefølge på hovedmenyen</string>
<string name="day">Dag</string>
<string name="week">Uke</string>
<string name="month">Måned</string>
<string name="quarter">Kvarter</string>
<string name="year">År</string>
<string name="total">Totalt</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">gang på</string>
<string name="every_x_days">Hver %d. dag</string>
<string name="every_x_weeks">Hver %d. uke</string>
<string name="every_x_months">Hver %d. måned</string>
<string name="score">Poengsum</string>
<string name="reminder_sound">Påminnelseslyd</string>
<string name="none">Ingen</string>
<string name="filter">Filtrér</string>
<string name="hide_completed">Gjem fullførte</string>
<string name="hide_archived">Gjem arkiverte</string>
<string name="sticky_notifications">Gjør varslinger klebrige</string>
<string name="sticky_notifications_description">Forhindrer varslinger fra å bli sveipet vekk.</string>
<string name="repair_database">Reparér database</string>
<string name="database_repaired">Database reparert.</string>
<string name="uncheck">Usjekk</string>
<string name="toggle">Veksl</string>
<string name="action">Handling</string>
<string name="habit">Vane</string>
<string name="sort">Sortér</string>
<string name="manually">Manuelt</string>
<string name="by_name">Etter navn</string>
<string name="by_color">Etter farge</string>
<string name="by_score">Etter poengsum</string>
<string name="download">Last ned</string>
<string name="export">Eksportér</string>
</resources>

View File

@@ -75,10 +75,10 @@
<string name="interval_4_hour">4 godziny</string>
<string name="interval_8_hour">8 godzin</string>
<string name="interval_24_hour">24 godziny</string>
<string name="pref_toggle_title">Przełącz powtarzanie przy krótkim naciśnięciu</string>
<string name="pref_toggle_description">Wygodniejsze ale może spowodować przypadkowe przełączenia.</string>
<string name="pref_toggle_title">Przełącz powtarzanie krótkim naciśnięciem</string>
<string name="pref_toggle_description">Wygodniejsze, ale może spowodować przypadkowe przełączenia.</string>
<string name="pref_snooze_interval_title">Czas drzemki między przypomnieniami</string>
<string name="pref_rate_this_app">Oceń tą aplikację w Google Play</string>
<string name="pref_rate_this_app">Oceń tę aplikację w Google Play</string>
<string name="pref_send_feedback">Prześlij uwagi do programisty</string>
<string name="pref_view_source_code">Zobacz kod źródłowy na GitHub\'ie</string>
<string name="pref_view_app_introduction">Zobacz wprowadzenie do aplikacji</string>

View File

@@ -47,7 +47,7 @@
<string name="reminder">Reamintire</string>
<string name="discard">Renunță</string>
<string name="save">Salvează</string>
<string name="streaks">Serii</string>
<string name="streaks">Zile consecutive</string>
<string name="no_habits_found">Nu ai niciun obicei activ.</string>
<string name="long_press_to_toggle">Apasă și ține pentru a bifa sau a debifa</string>
<string name="reminder_off">Dezactivat</string>
@@ -90,6 +90,7 @@
<string name="hint_landscape">Poți vedea mai multe zile în modul peisaj.</string>
<string name="delete_habits">Șterge obiceiuri</string>
<string name="delete_habits_message">Obiceiurile vor fi șterse permanent. Această acțiune nu este reversibilă.</string>
<string name="habit_not_found">Obicei şters / negăsit</string>
<string name="weekends">Weekenduri</string>
<string name="any_weekday">Zile de lucru</string>
<string name="any_day">Orice zi</string>
@@ -106,8 +107,8 @@
<string name="frequency">Frecvență</string>
<string name="checkmark">Bifă</string>
<string name="strength">Putere</string>
<string name="best_streaks">Cele mai bune serii</string>
<string name="current_streaks">Seria curentă</string>
<string name="best_streaks">Cele mai multe zile consecutive</string>
<string name="current_streaks">Numărul curent de reușite succesive</string>
<string name="number_of_repetitions">Număr de repetiții</string>
<string name="last_x_days">Ultimele %d zile</string>
<string name="last_x_weeks">Ultimele %d săptămâni</string>
@@ -133,5 +134,24 @@
<string name="bug_report_failed">Generare raport de erori nereușită.</string>
<string name="generate_bug_report">Generează raport de erori</string>
<string name="troubleshooting">Depanare</string>
<string name="reverse_days">Inversează ordinea zilelor</string>
<string name="day">Zi</string>
<string name="week">Săptămână</string>
<string name="month">Lună</string>
<string name="quarter">Trimestru</string>
<string name="year">An</string>
<string name="total">Total</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">dată la</string>
<string name="every_x_days">La fiecare %d zile</string>
<string name="every_x_weeks">La fiecare %d săptămâni</string>
<string name="every_x_months">La fiecare %d luni</string>
<string name="hide_completed">Ascunde cele completate</string>
<string name="hide_archived">Ascunde cele arhivate</string>
<string name="uncheck">Debifează</string>
<string name="habit">Obicei</string>
<string name="sort">Sortează</string>
<string name="manually">Manual</string>
<string name="by_name">După nume</string>
<string name="by_color">După culoare</string>
</resources>

View File

@@ -43,7 +43,7 @@
<string name="clear">Очистить</string>
<string name="description_hint">Вопрос (пример: \"Делали ли вы сегодня зарядку?\")</string>
<string name="repeat">Повторять</string>
<string name="times_every">раза за</string>
<string name="times_every">раз в</string>
<string name="days">дней</string>
<string name="reminder">Напоминание</string>
<string name="discard">Отменить</string>
@@ -154,7 +154,7 @@
<string name="every_x_days">Каждые %d дней</string>
<string name="every_x_weeks">Каждые %d недель</string>
<string name="every_x_months">Каждые %d месяцев</string>
<string name="score">Стабильность</string>
<string name="score">Счет</string>
<string name="reminder_sound">Звук напоминания</string>
<string name="none">Без звука</string>
<string name="filter">Фильтр</string>
@@ -172,7 +172,7 @@
<string name="manually">Вручную</string>
<string name="by_name">По названию</string>
<string name="by_color">По цвету</string>
<string name="by_score">По стабильности</string>
<string name="by_score">По оценке</string>
<string name="download">Загрузить</string>
<string name="export">Экспортировать</string>
</resources>

View File

@@ -19,18 +19,18 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">Loop Sledilnik Navad</string>
<string name="app_name">Loop Navade</string>
<string name="main_activity_title">Navade</string>
<string name="action_settings">Nastavitve</string>
<string name="edit">Spremeni</string>
<string name="edit">Uredi</string>
<string name="delete">Izbriši</string>
<string name="archive">Arhiviraj</string>
<string name="unarchive">Odarhiviraj</string>
<string name="add_habit">Dodaj navado</string>
<string name="color_picker_default_title">Spremeni barvo</string>
<string name="toast_habit_created">Navada ustvarjana.</string>
<string name="toast_habit_deleted">Navada izbrisana.</string>
<string name="toast_habit_restored">Navada obnovljena.</string>
<string name="toast_habit_created">Navada ustvarjena</string>
<string name="toast_habit_deleted">Navada izbrisana</string>
<string name="toast_habit_restored">Navada obnovljena</string>
<string name="toast_nothing_to_undo">Nič za razveljaviti.</string>
<string name="toast_nothing_to_redo">Nič za ponovno opraviti.</string>
<string name="toast_habit_changed">Navada spremenjena.</string>

View File

@@ -19,79 +19,82 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">Loop Habit Tracker</string>
<string name="main_activity_title">Vanor</string>
<string name="action_settings">Inställningar</string>
<string name="edit">Redigera</string>
<string name="delete">Ta bort</string>
<string name="archive">Arkivera</string>
<string name="unarchive">Avarkivera</string>
<string name="unarchive">Ångra arkivering</string>
<string name="add_habit">Lägg till vana</string>
<string name="color_picker_default_title">Byt färg</string>
<string name="toast_habit_created">Vana skapad</string>
<string name="toast_habit_deleted">Vana borttagen</string>
<string name="toast_habit_restored">Vana återställd</string>
<string name="toast_habit_deleted">Vanor borttagna</string>
<string name="toast_habit_restored">Vanor återställda</string>
<string name="toast_nothing_to_undo">Inget att ångra</string>
<string name="toast_nothing_to_redo">Inget att göra om</string>
<string name="toast_nothing_to_redo">Inget att ångra</string>
<string name="toast_habit_changed">Vana ändrad</string>
<string name="toast_habit_changed_back">Vana ändrad tillbaka</string>
<string name="toast_habit_changed_back">Vana ändrad</string>
<string name="toast_habit_archived">Vanor arkiverade</string>
<string name="toast_habit_unarchived">Vanor avakriverade</string>
<string name="toast_habit_unarchived">Vanor återställda</string>
<string name="overview">Översikt</string>
<string name="habit_strength">Vanestyrka</string>
<string name="history">Historik</string>
<string name="clear">Rensa</string>
<string name="description_hint">Fråga (Har du ... idag?)</string>
<string name="repeat">Upprepa</string>
<string name="times_every">gånger under</string>
<string name="days">dag</string>
<string name="times_every">gånger i</string>
<string name="days">dagar</string>
<string name="reminder">Påminnelse</string>
<string name="discard">Avbryt</string>
<string name="save">Spara</string>
<string name="streaks">Framgångar</string>
<string name="streaks">Streaks</string>
<string name="no_habits_found">Du har inga aktiva vanor</string>
<string name="long_press_to_toggle">Tryck och håll in för att markera och avmarkera</string>
<string name="long_press_to_toggle">Tryck-och-håll för att markera eller avmarkera</string>
<string name="reminder_off">Av</string>
<string name="validation_name_should_not_be_blank">Namn får inte vara blank.</string>
<string name="validation_number_should_be_positive">Nummer måste vara positiv.</string>
<string name="validation_at_most_one_rep_per_day">Du kan ha max en upprepning per dag</string>
<string name="validation_name_should_not_be_blank">Namn får inte vara tomt.</string>
<string name="validation_number_should_be_positive">Talet måste vara positivt.</string>
<string name="validation_at_most_one_rep_per_day">Du kan ha högst en repetition per dag</string>
<string name="create_habit">Skapa vana</string>
<string name="edit_habit">Redigera vana</string>
<string name="check">Markera</string>
<string name="snooze">Senare</string>
<!-- App introduction -->
<string name="intro_title_1">Välkommen</string>
<string name="intro_description_1">Loop Habit Tracker Hjälper dig att skapa och underhålla bra vanor.</string>
<string name="intro_description_1">Loop Habit Tracker hjälper dig att skapa och bibehålla goda vanor.</string>
<string name="intro_title_2">Skapa några nya vanor</string>
<string name="intro_description_2">Varje dag, efter att du utfört dina vanor, lägg till en markering i appen.</string>
<string name="intro_title_3">Fortsätt göra det</string>
<string name="intro_description_3">Vanor som utförs konstant över en lång period kommer att tjäna dig en full stjärna.</string>
<string name="intro_description_2">Efter varje dag när du utfört din vana så markerar du den i appen.</string>
<string name="intro_title_3">Fortsätt att göra det</string>
<string name="intro_description_3">Vanor som du utför konsekvent över en lång period ger dig en stjärna.</string>
<string name="intro_title_4">Följ dina framsteg</string>
<string name="intro_description_4">Detaljerade grafer visar dig hur dina vanor har utvecklats över tid.</string>
<string name="intro_description_4">Detaljerade grafer visar dig hur dina vanor förbättras över tid.</string>
<string name="interval_15_minutes">15 minuter</string>
<string name="interval_30_minutes">30 minuter</string>
<string name="interval_1_hour">1 timme</string>
<string name="interval_2_hour">2 timmar</string>
<string name="interval_4_hour">4 timmar</string>
<string name="interval_8_hour">8 timmar</string>
<string name="pref_toggle_title">Aktivera repetitioner med kort tryckning</string>
<string name="pref_toggle_description">Mer bekvämt, men kan orsaka oavsiktliga aktiveringar. </string>
<string name="pref_snooze_interval_title">Snooza intervall på påminnelsen</string>
<string name="pref_rate_this_app">Betygsätt denna app på Google Play</string>
<string name="pref_send_feedback">Skicka feedback till utvecklaren</string>
<string name="pref_view_source_code">Visa källkod på Github</string>
<string name="pref_view_app_introduction">Visa appintroduktion</string>
<string name="interval_24_hour">24 timmar</string>
<string name="pref_toggle_title">Växla med snabb tryckning</string>
<string name="pref_toggle_description">Markera genom att trycka snabbt istället för att hålla ner. Mer bekvämt, men kan orsaka oavsiktliga aktiveringar.</string>
<string name="pref_snooze_interval_title">Standard tid på snooze</string>
<string name="pref_rate_this_app">Betygsätt oss på Google Play</string>
<string name="pref_send_feedback">Skicka feedback till utvecklarna</string>
<string name="pref_view_source_code">Visa källkod på GitHub</string>
<string name="pref_view_app_introduction">Visa introduktion till appen</string>
<string name="links">Länkar</string>
<string name="behavior">Beteende</string>
<string name="name">Namn</string>
<string name="settings">Inställningar</string>
<string name="snooze_interval">Snooze intervall</string>
<string name="hint_title">Visste du? </string>
<string name="hint_drag">För att arrangera inlägg, tryck och håll in på namnet av inlägget, dra den sedan till rätt plats.</string>
<string name="hint_landscape">Du kan se fler dagar genom att hålla telefon i landskapsläge.</string>
<string name="snooze_interval">Intervall snooze</string>
<string name="hint_title">Visste du att?</string>
<string name="hint_drag">För att flytta på poster - tryck och håll nere på den vana du vill flytta, sedan flytta den till önskad plats.</string>
<string name="hint_landscape">Du kan se mer dagar genom att hålla telefonen i landskapsläge.</string>
<string name="delete_habits">Ta bort vanor</string>
<string name="delete_habits_message">Vanorna kommer att tas bort permanent. Denna åtgärd kan inte ångras.</string>
<string name="delete_habits_message">Vanorna kommer att tas bort permanent. Du kan inte ångra detta.</string>
<string name="habit_not_found">Vana borttagen / hittades inte</string>
<string name="weekends">Helger</string>
<string name="any_weekday">Veckodagar</string>
<string name="any_weekday">Måndag till fredag</string>
<string name="any_day">Vilken dag som helst</string>
<string name="select_weekdays">Välj dagar</string>
<string name="export_to_csv">Exportera data (CSV)</string>
@@ -102,11 +105,12 @@
<string name="about">Om</string>
<string name="translators">Översättare</string>
<string name="developers">Utvecklare</string>
<string name="version_n">Version %s</string>
<string name="frequency">Frekvens</string>
<string name="checkmark">Check</string>
<string name="checkmark">Kryssruta</string>
<string name="strength">Styrka</string>
<string name="best_streaks">Bästa framgångar</string>
<string name="current_streaks">Nuvarande framgång</string>
<string name="best_streaks">Bästa streak</string>
<string name="current_streaks">Nuvarande streak</string>
<string name="number_of_repetitions">Antal repetitioner</string>
<string name="last_x_days">Sista %d dagarna</string>
<string name="last_x_weeks">Sista %d veckorna</string>
@@ -119,18 +123,56 @@
<string name="five_times_per_week">5 gånger per vecka</string>
<string name="custom_frequency">Anpassa ...</string>
<string name="help">Hjälp och FAQ</string>
<string name="could_not_export">Misslyckades med att exportera data.</string>
<string name="could_not_import">Misslyckades med att importera data.</string>
<string name="file_not_recognized">Fil inte igenkänd.</string>
<string name="habits_imported">Vanor importerade.</string>
<string name="full_backup_success">Backup har exporterats.</string>
<string name="could_not_export">Det gick inte att exportera.</string>
<string name="could_not_import">Det gick inte att importera.</string>
<string name="file_not_recognized">Kunde inte känna igen filen.</string>
<string name="habits_imported">Import av dina vanor lyckades.</string>
<string name="full_backup_success">Fullständig backup har exporterats.</string>
<string name="import_data">Importera data</string>
<string name="export_full_backup">Exportera backup</string>
<string name="import_data_summary">Stöder backuper exporterade av den här appen, så väl filer genererade av Tickmate, Habitbull eller Rewire. Läs FAQ för mer information.</string>
<string name="export_as_csv_summary">Genererade filer kan öppnas av mjukvara som Microsoft Excel eller Openoffice Calc. Denna filen kan inte importeras tillbaka.</string>
<string name="export_full_backup_summary">Genererar en fil som innehåller all din data. Den här filen kan importeras tillbaka.</string>
<string name="bug_report_failed">Misslyckades med att generera bugrapport. </string>
<string name="generate_bug_report">Generera bugrapport</string>
<string name="import_data_summary">Appen har stöd att importera data från denna app, men även backuper från Tickmate, Habitbull eller Rewire. Läs FAQ om du vill meta mer.</string>
<string name="export_as_csv_summary">Filerna genererade kan öppnas i Microsoft Excel eller OpenOffice Calc. Däremot kan denna fil inte importeras tillbaka till appen.</string>
<string name="export_full_backup_summary">Generera en fil som innehåller all din data. Denna fil kan importas tillbaka till appen.</string>
<string name="bug_report_failed">Det gick inte att generera felrapport.</string>
<string name="generate_bug_report">Generera felrapport</string>
<string name="troubleshooting">Felsökning</string>
<string name="help_translate">Hjälp till att översätta appen</string>
<string name="night_mode">Nattläge</string>
<string name="use_pure_black">Använd svart färg i nattläge</string>
<string name="pure_black_description">Ersätter gråa bakgrunder med svart färg i nattläge. Reducerar batterianvändningen för telefoner med AMOLED-skärm.</string>
<string name="interface_preferences">Gränssnitt</string>
<string name="reverse_days">Omvänd ordning på dagar</string>
<string name="reverse_days_description">Visar dagar i omvänd ordning i appen</string>
<string name="day">Dag</string>
<string name="week">Vecka</string>
<string name="month">Månad</string>
<string name="quarter">Kvartal</string>
<string name="year">År</string>
<string name="total">Totalt</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">gånger var</string>
<string name="every_x_days">Var %d:e dag</string>
<string name="every_x_weeks">Var %d:e vecka</string>
<string name="every_x_months">Var %d:e månad</string>
<string name="score">Poäng</string>
<string name="reminder_sound">Påminnelseljud</string>
<string name="none">Inget</string>
<string name="filter">Filtrera</string>
<string name="hide_completed">Dölj slutförda</string>
<string name="hide_archived">Dölj arkiverade</string>
<string name="sticky_notifications">Gör notifikationer permanenta</string>
<string name="sticky_notifications_description">Förhindrar att notifikationer stängs.</string>
<string name="repair_database">Reparera databas</string>
<string name="database_repaired">Databasen reparerad.</string>
<string name="uncheck">Avmarkera</string>
<string name="toggle">Växla</string>
<string name="action">Åtgärd</string>
<string name="habit">Vana</string>
<string name="sort">Sortera</string>
<string name="manually">Manuellt</string>
<string name="by_name">Efter namn</string>
<string name="by_color">Efter färg</string>
<string name="by_score">Efter poäng</string>
<string name="download">Ladda ner</string>
<string name="export">Exportera</string>
</resources>

View File

@@ -0,0 +1,178 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.com-->
<!--
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
~
~ This file is part of Loop Habit Tracker.
~
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by the
~ Free Software Foundation, either version 3 of the License, or (at your
~ option) any later version.
~
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
~ more details.
~
~ You should have received a copy of the GNU General Public License along
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<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_habit_restored">பழக்கம் மீட்கப்பட்டது</string>
<string name="toast_nothing_to_undo">மீட்க ஒன்றும் இல்லை</string>
<string name="toast_nothing_to_redo">திருத்த எதுவும் இல்லை</string>
<string name="toast_habit_changed">பழக்கம் மாற்றப்பட்டது</string>
<string name="toast_habit_changed_back">பழக்கம் திரும்ப பழைய நிலைக்கு மாற்றப்பட்டது</string>
<string name="toast_habit_archived">காப்பகப்படுத்தியப் பழக்கம்</string>
<string name="toast_habit_unarchived">பழக்கங்கள் ஆவண காப்பகத்தில் இருந்து நீக்கப் பட்டது</string>
<string name="overview">மேற்பார்வை</string>
<string name="habit_strength">பழக்கத்தின் வலிமை</string>
<string name="history">வரலாறு</string>
<string name="clear">அழி</string>
<string name="description_hint">கேள்வி (இன்று ... செய்தீர்களா?)</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="interval_24_hour">24 மணி நேரம்</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">Google 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="settings">அமைப்புகள்</string>
<string name="snooze_interval">தாமத காலம்</string>
<string name="hint_title">உங்களுக்கு தெரியுமா?</string>
<string name="hint_drag">பதிவுகளை மறுசீரைமக்க, தேவையான பழக்க பதிவின் மீது அழுத்தி பிடித்து பின் தேவையான இடத்திற்கு அதை இழுக்கவும்.</string>
<string name="hint_landscape">உங்கள் கைப்பேசியை அகலவாக்கில் வைக்கும்போது இன்னும் அதிக நாட்களை காண முடியும்.</string>
<string name="delete_habits">பழக்கங்களை நீக்கவும்</string>
<string name="delete_habits_message">பழக்கங்கள் நிரந்தரமாக நீக்கப்படும். இந்த செயலை மீட்டமைக்க இயலாது.</string>
<string name="habit_not_found">பழக்கம் நீக்கப்பட்டுவிட்டது / காணவில்லை</string>
<string name="weekends">வார இறுதிகள்</string>
<string name="any_weekday">திங்கள் முதல் வெள்ளி வரை</string>
<string name="any_day">வாரத்தின் எந்த நாளிலும்</string>
<string name="select_weekdays">நாட்களை தேர்வு செய்யவும்</string>
<string name="export_to_csv">CSV நிரல் வகையில் ஏற்றுமதி செய்யவும்</string>
<string name="done_label">முடிந்தது</string>
<string name="clear_label">அழி</string>
<string name="select_hours">மணி நேரங்களை தேர்வு செய்யவும்</string>
<string name="select_minutes">நிமிடங்களை தேர்வு செய்யவும்</string>
<string name="about">இதை பற்றி</string>
<string name="translators">மொழிப்பெயர்ப்பாளர்கள்</string>
<string name="developers">மென்பொருள் ஆசிரியர்கள்</string>
<string name="version_n">மென்பொருள் பதிப்பு %s</string>
<string name="frequency">கால இடைவெளி</string>
<string name="checkmark">சரிபார்ப்பு குறி</string>
<string name="strength">வலிமை</string>
<string name="best_streaks">சிறந்த சாதனைகள்</string>
<string name="current_streaks">நடப்பு சாதனை</string>
<string name="number_of_repetitions">மீள் நிகழ்வுகளின் எண்ணிக்கை</string>
<string name="last_x_days">கடந்த %d நாட்கள்</string>
<string name="last_x_weeks">கடந்த %d வாரங்கள்</string>
<string name="last_x_months">கடந்த %d மாதங்கள்</string>
<string name="last_x_years">கடந்த %d வருடங்கள்</string>
<string name="all_time">எல்லா நேரமும்</string>
<string name="every_day">எல்லா நாளும்</string>
<string name="every_week">எல்லா வாரமும்</string>
<string name="two_times_per_week">வாரத்திற்கு இரண்டு முறை</string>
<string name="five_times_per_week">வாரத்துக்கு 5 முறை</string>
<string name="custom_frequency">விருப்பத்திற்கு ஏற்றபடி&#8230;</string>
<string name="help">உதவி &amp; அதிகம் கேட்கப்படும் கேள்விகள்</string>
<string name="could_not_export">தரவுகளை ஏற்றுமதி செய்ய முடியவில்லை.</string>
<string name="could_not_import">தரவை இறக்குமதி செய்ய முடியவில்லை.</string>
<string name="file_not_recognized">இது எந்த வகையான ஆவணம் என்பதை உறுதி செய்ய முடியவில்லை.</string>
<string name="habits_imported">பழக்கங்களை வெற்றிகரமாக இறக்குமதி செய்யப்பட்டது.</string>
<string name="full_backup_success">முழு ஆவணக் காப்பு நகல் வெற்றிகரமாக ஏற்றுமதி செய்யப்பட்டது.</string>
<string name="import_data">தரவு இறக்குமதி</string>
<string name="export_full_backup">ஆவணக் காப்பு நகல் முழுமையாக ஏற்றுமதி செய்</string>
<string name="import_data_summary">இந்த செயலி மூலம் ஆவண காப்பு நகல் முழுவதுமாக ஏற்றுமதி செய்யவும் மற்றும் Tickmate, HabitBull அல்லது Rewire செயலிகள் மூலம் உருவாக்கப்படும் ஆவணங்களும் இந்த செயலியில் பயண்படுத்தலாம் மேலும் தகவல்களுக்கு அதிகம் கேட்கப்படும் கேள்விகளை (FAQ) பார்க்கவும்.</string>
<string name="export_as_csv_summary">உருவாக்கப்பட்ட ஆவணங்களை விரித்தாள் மென்பொருள்களான Microsoft Excel அல்லது OpenOffice Calc மூலம் திறக்கலாம். ஆனால் இவற்றை திரும்ப இறக்குமதி செய்ய முடியாது.</string>
<string name="export_full_backup_summary">உங்களின் அனைத்து தரவுகளையும் கொண்ட ஒரு ஆவணம் உருவாக்கப்படும். இந்த ஆவணத்தை மீண்டும் இந்த செயலியில் இறக்குமதி செய்யலாம்.</string>
<string name="bug_report_failed">செயலி பிழை அறிக்கை உருவாக்க முடியவில்லை.</string>
<string name="generate_bug_report">பிழை அறிக்கை உருவாக்கு</string>
<string name="troubleshooting">பழுது இடமறிதல்</string>
<string name="help_translate">இந்த செயலியை மற்ற மொழிகளில் மொழிபெயர்க்க உதவி செய்யவும்</string>
<string name="night_mode">இருள் வண்ண பாங்கு</string>
<string name="use_pure_black">இருள் பாங்கில் முழு கருப்பு நிறத்தை பயண்படுத்து</string>
<string name="pure_black_description">இதன் மூலம் செயலியில் உள்ள பழுப்பு பின்புலங்கள் நீக்கப்பட்டு முழுவதும் கருப்பு நிற பின்புலங்களாக மாற்றப்படும். இது AMOLED திரை கொண்ட கைப்பேசிகளில் மின்கல பயன்பாட்டை குறைக்கும்.</string>
<string name="interface_preferences">இடைமுகம்</string>
<string name="reverse_days">தலைகீழ் வரிசையில் நாட்கள்</string>
<string name="reverse_days_description">பிரதான திரையில் நாட்களை தலை கீழ் வரிசையில் காட்டு</string>
<string name="day">நாள்</string>
<string name="week">வாரம்</string>
<string name="month">மாதம்</string>
<string name="quarter">காற் பங்கு</string>
<string name="year">வருடம்</string>
<string name="total">மொத்தம்</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">நேரத்தை</string>
<string name="every_x_days">ஓவ்வொரு %d நாளும்</string>
<string name="every_x_weeks">ஒவ்வொரு %d வாரங்களும்</string>
<string name="every_x_months">ஒவ்வொரு %d மாதங்களு</string>
<string name="score">மதிப்பெண்கள்</string>
<string name="reminder_sound">நினைவூட்டல் சத்தம்</string>
<string name="none">எதுவும் இல்லை</string>
<string name="filter">வடிகட்டவும்</string>
<string name="hide_completed">மறைத்தல் முடிந்தது</string>
<string name="hide_archived">ஆவணக் காப்பை மறைக்கவும்</string>
<string name="sticky_notifications">நினைவூட்டல்களை நிலைத்து நிற்க வை</string>
<string name="sticky_notifications_description">நினைவூட்டல்களை விரல்களால் தள்ளி விட முடியாத படி செய்கிறது.</string>
<string name="repair_database">தரவு தளத்தை பழுது பார்க்கவும்</string>
<string name="database_repaired">தரவுதளம் பழுதடைந்து விட்டது.</string>
<string name="uncheck">சரிப்பார்க்காமல் அப்படியே விடு</string>
<string name="toggle">தாவு</string>
<string name="action">செயல்</string>
<string name="habit">பழக்கம்</string>
<string name="sort">வரிசைப்படுத்தவும்</string>
<string name="manually">கைமுறை</string>
<string name="by_name">பெயரின் மூலம்</string>
<string name="by_color">நிறத்தின் மூலம்</string>
<string name="by_score">மதிப்பெண்களின் மூலம்</string>
<string name="download">பதிவிறக்கம்</string>
<string name="export">ஏற்றுமதி</string>
</resources>

View File

@@ -30,27 +30,27 @@
<string name="color_picker_default_title">Renk Değiştir</string>
<string name="toast_habit_created">Alışkanlık oluşturuldu.</string>
<string name="toast_habit_deleted">Alışkanlık silindi.</string>
<string name="toast_habit_restored">Alışkanlık</string>
<string name="toast_nothing_to_undo">Geri alınacak birşey yok.</string>
<string name="toast_habit_restored">Alışkanlıklar geri getirildi</string>
<string name="toast_nothing_to_undo">Geri alınacak bir şey yok</string>
<string name="toast_nothing_to_redo">Tekrar yapılacak birşey yok.</string>
<string name="toast_habit_changed">Alışkanlık değişti.</string>
<string name="toast_habit_changed_back">Alışkanlık eski haline getirildi.</string>
<string name="toast_habit_changed">Alışkanlık değiştirildi</string>
<string name="toast_habit_changed_back">Alışkanlık eski haline getirildi</string>
<string name="toast_habit_archived">Alışkanlık arşivlendi.</string>
<string name="toast_habit_unarchived">Alışkanlık arşivden çıkarıldı.</string>
<string name="overview">Genel bakış</string>
<string name="habit_strength">Alışkanlık dayanımı</string>
<string name="overview">Genel Bakış</string>
<string name="habit_strength">Alışkanlık gücü</string>
<string name="history">Geçmiş</string>
<string name="clear">Temizle</string>
<string name="description_hint">Soru (Bugün ... yaptın mı?)</string>
<string name="repeat">Tekrar</string>
<string name="times_every">kez</string>
<string name="days">günde</string>
<string name="times_every">defa /</string>
<string name="days">gün</string>
<string name="reminder">Hatırlatma</string>
<string name="discard">İptal</string>
<string name="discard">Vazgeç</string>
<string name="save">Kaydet</string>
<string name="streaks">Etkinlikler</string>
<string name="streaks">Seriler</string>
<string name="no_habits_found">Etkin alışkanlığın yok</string>
<string name="long_press_to_toggle">Yapıldı veya yapılmadı işareti koymak için uzun basılı tut</string>
<string name="long_press_to_toggle">İşaretlemek ya da işaret kaldırmak için basılı tut</string>
<string name="reminder_off">Kapalı</string>
<string name="validation_name_should_not_be_blank">Adı boş bırakamazsın.</string>
<string name="validation_number_should_be_positive">Sayılar sıfırdan büyük olmalı.</string>
@@ -61,13 +61,13 @@
<string name="snooze">Sonra</string>
<!-- App introduction -->
<string name="intro_title_1">Hoşgeldin</string>
<string name="intro_description_1">İyi alışkanlıklar edinmek ve devam etmene yardımcı olur.</string>
<string name="intro_description_1">Loop Alışkanlık Takibi, iyi alışkanlıklar edinmene ve sürdürmene yardımcı olur.</string>
<string name="intro_title_2">Yeni alışkanlıklar oluştur</string>
<string name="intro_description_2">Her gün, alışkanlığını gerçekleştirdikten sonra, uygulamada onay işareti koy.</string>
<string name="intro_title_3">Yapmaya devam et</string>
<string name="intro_description_3">Uzun bir süre ile sürekli yaptığın alışkanlıkların için tam yıldız kazanacaksın.</string>
<string name="intro_description_3">Uzun süre düzenli sürdürdüğün alışkanlıkların için bir tam yıldız kazanacaksın.</string>
<string name="intro_title_4">Gelişimini izle</string>
<string name="intro_description_4">Detaylı grafiklerle, zaman içinde alışkanlıklarını nasıl geliştiğini gör.</string>
<string name="intro_description_4">Detaylı grafiklerle, zaman içinde alışkanlıklarının nasıl geliştiğini gör.</string>
<string name="interval_15_minutes">15 dakika</string>
<string name="interval_30_minutes">30 dakika</string>
<string name="interval_1_hour">1 saat</string>
@@ -75,25 +75,26 @@
<string name="interval_4_hour">4 saat</string>
<string name="interval_8_hour">8 saat</string>
<string name="interval_24_hour">24 saat</string>
<string name="pref_toggle_title">Daha kısa süre basma ile yapıldı/yapılmadı işaretleme</string>
<string name="pref_toggle_description">Daha kullanışlı ama kazara istenmeyen işaretlemeler olabilir</string>
<string name="pref_toggle_title">Kısa dokunuşla işaretle</string>
<string name="pref_toggle_description">Alışkanlıklarını basılı tutmak yerine tek dokunuşla işaretlemeni sağlar. Kullanımı daha rahattır ama kaza eseri işaretleme yapabilirsin.</string>
<string name="pref_snooze_interval_title">Hatırlatmalardaki erteleme süresi</string>
<string name="pref_rate_this_app">Google Play\'de uygulamayı oyla</string>
<string name="pref_send_feedback">Geliştiriciye geri bildirim gönder</string>
<string name="pref_view_source_code">Github\'da kaynak kodunu bak</string>
<string name="pref_view_source_code">Github\'da kaynak kodunu gör</string>
<string name="pref_view_app_introduction">Uygulama tanıtımını göster</string>
<string name="links">Bağlantılar</string>
<string name="behavior">Davranış</string>
<string name="name">Ad</string>
<string name="settings">Ayarlar</string>
<string name="snooze_interval">Erteleme süresi</string>
<string name="hint_title">Biliyor musun?</string>
<string name="hint_drag">Girdileri yeniden düzenlemek için, alışkanlık adının üstüne bas ve doğru yere sürükle.</string>
<string name="hint_title">Biliyor muydun?</string>
<string name="hint_drag">Girdileri sıralamak için, alışkanlık adının üstüne basılı tut ve doğru yere sürükle.</string>
<string name="hint_landscape">Cihazını yatay tutarak daha fazla gün görebilirsin.</string>
<string name="delete_habits">Alışkanlıkları Sil</string>
<string name="delete_habits_message">Alışkanlıklar kalıcı olarak silinecek. Bu eylem geri alınamaz.</string>
<string name="habit_not_found">Alışkanlık silinmiş ya da bulunamadı</string>
<string name="weekends">Hafta sonları</string>
<string name="any_weekday">Pazartesinden Cumaya</string>
<string name="any_weekday">Pazartesi-Cuma</string>
<string name="any_day">Haftanın herhangi bir günü</string>
<string name="select_weekdays">Günleri seç</string>
<string name="export_to_csv">CSV olarak dışa aktar</string>
@@ -106,10 +107,10 @@
<string name="developers">Geliştiriciler</string>
<string name="version_n">Sürüm %s</string>
<string name="frequency">Sıklık</string>
<string name="checkmark">Yapıldı işareti</string>
<string name="strength">Dayanım</string>
<string name="best_streaks">En iyi etkinlik günü</string>
<string name="current_streaks">Bugünkü etkinlik</string>
<string name="checkmark">İşaret</string>
<string name="strength">Güç</string>
<string name="best_streaks">En uzun seriler</string>
<string name="current_streaks">Şimdiki seri</string>
<string name="number_of_repetitions">Tekrar sayısı</string>
<string name="last_x_days">Son %d gün</string>
<string name="last_x_weeks">Son %d hafta</string>
@@ -125,48 +126,53 @@
<string name="could_not_export">Dışarı veri aktarımı başarısız.</string>
<string name="could_not_import">İçeri veri aktarımı başarısız.</string>
<string name="file_not_recognized">Dosya tanınamadı.</string>
<string name="habits_imported">Alışkanlıklar başarılı içeri aktarıldı.</string>
<string name="habits_imported">Alışkanlıklar başarıyla içeri aktarıldı.</string>
<string name="full_backup_success">Tam yedek başarıyla dışarı aktarıldı.</string>
<string name="import_data">Veri eri aktar</string>
<string name="import_data">İçeri veri aktar</string>
<string name="export_full_backup">Tüm yedeği dışarı aktar</string>
<string name="import_data_summary">Hem bu uygulama tarafından dışarı aktarılmış tam yedekleri, hem de Tickmate, HabitBull veya Rewire tarafından üretilmiş dosyaları destekler. Daha fazla bilgi için SSS bakın.</string>
<string name="import_data_summary">Hem bu uygulama tarafından dışarı aktarılmış tam yedekleri, hem de Tickmate, HabitBull veya Rewire tarafından üretilmiş dosyaları destekler. Daha fazla bilgi için SSS\'a başvurun.</string>
<string name="export_as_csv_summary">Üretilen dosyalar, Microsoft Excel veya OpenOffice Calc. gibi hesap taplosu uygulamaları ile açılabilir. Bu dosya yeniden içeri aktarılamaz.</string>
<string name="export_full_backup_summary">Üretilen dosya, tüm verilerini içerir. Bu dosya yeniden içeri aktarılabilir.</string>
<string name="export_full_backup_summary">Tüm verilerini içeren bir dosya üretir. Bu dosya yeniden içeri aktarılabilir.</string>
<string name="bug_report_failed">Hata raporu oluşturulamadı.</string>
<string name="generate_bug_report">Hata raporu üret</string>
<string name="troubleshooting">Sorun Giderme</string>
<string name="help_translate">Bu uygulamanın çevirisine yardım et</string>
<string name="night_mode">Gece kipi</string>
<string name="use_pure_black">Gece kipinde saf siyah kullan</string>
<string name="pure_black_description">Gece kipinde gri arkaplanını, saf siyah ile değiştir. AMOLED ekranlı cihazlarda pil kullanımını düşür.</string>
<string name="pure_black_description">Gece kipinde gri arkaplanını, saf siyah ile değiştir. AMOLED ekranlı cihazlarda pil kullanımını azaltabilir.</string>
<string name="interface_preferences">Arayüz</string>
<string name="reverse_days">Günleri ters sırala</string>
<string name="reverse_days_description">Ana ekranda günleri tersen göster</string>
<string name="reverse_days_description">Ana ekranda günleri tersten göster</string>
<string name="day">Gün</string>
<string name="week">Hafta</string>
<string name="month">Ay</string>
<string name="quarter">3 Ay</string>
<string name="year">Yıl</string>
<string name="total">Tümü</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">kez</string>
<string name="time_every">defa /</string>
<string name="every_x_days">Her %d gün</string>
<string name="every_x_weeks">Her %d hafta</string>
<string name="every_x_months">Her %d ay</string>
<string name="score">Puan</string>
<string name="reminder_sound">Hatırlatma sesi</string>
<string name="none">Sessiz</string>
<string name="filter">Filtre</string>
<string name="hide_completed">Tamamlananları gizle</string>
<string name="hide_archived">Arşivlenenleri gizle</string>
<string name="repair_database">Verıtabanını onar</string>
<string name="database_repaired">Verıtabanı onarıldı.</string>
<string name="uncheck">Yapmadım</string>
<string name="sticky_notifications">Bildirimleri kalıcı yap</string>
<string name="sticky_notifications_description">Bildirimlerin kaydırılarak temizlenmesini engelle.</string>
<string name="repair_database">Veritabanını onar</string>
<string name="database_repaired">Veritabanı onarıldı.</string>
<string name="uncheck">İşareti kaldır</string>
<string name="toggle">Değiştir</string>
<string name="action">Eylem</string>
<string name="habit">Alışkanlık</string>
<string name="sort">Sırala</string>
<string name="manually">Elle</string>
<string name="by_name">Ad</string>
<string name="by_color">Renk</string>
<string name="by_score">Puan</string>
<string name="by_name">Ada göre</string>
<string name="by_color">Renge göre</string>
<string name="by_score">Puana göre</string>
<string name="download">İndir</string>
<string name="export">Dışarı aktar</string>
</resources>

View File

@@ -25,7 +25,7 @@
<string name="edit">Змінити</string>
<string name="delete">Вилучити</string>
<string name="archive">Архівувати</string>
<string name="unarchive">Повернути з архіву</string>
<string name="unarchive">Розархівувати</string>
<string name="add_habit">Додати звичку</string>
<string name="color_picker_default_title">Змінити колір</string>
<string name="toast_habit_created">Звичку створено</string>
@@ -63,11 +63,11 @@
<string name="intro_title_1">Ласкаво просимо</string>
<string name="intro_description_1">Трекер звичок Loop допомагає вам розвивати і підтримувати корисні звички.</string>
<string name="intro_title_2">Додайте нові звички</string>
<string name="intro_description_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="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>

View File

@@ -19,6 +19,160 @@
~ with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<resources>
<string name="app_name">Trình theo dõi thói quen Loop</string>
<string name="main_activity_title">Thói quen</string>
<string name="action_settings">Cài đặt</string>
<string name="edit">Chỉnh sửa</string>
<string name="delete">Xoá</string>
<string name="archive">Lưu trữ</string>
<string name="unarchive">Hủy lưu trữ</string>
<string name="add_habit">Thêm thói quen</string>
<string name="color_picker_default_title">Thay đổi màu sắc</string>
<string name="toast_habit_created">Thói quen đã được tạo</string>
<string name="toast_habit_deleted">Thói quen đã bị xóa</string>
<string name="toast_habit_restored">Thói quen đã được phục hồi</string>
<string name="toast_nothing_to_undo">Không có gì để hoàn tác</string>
<string name="toast_nothing_to_redo">Không có gì để khôi phục</string>
<string name="toast_habit_changed">Thói quen đã được thay đổi</string>
<string name="toast_habit_changed_back">Thói quen đã huỷ chỉnh sửa</string>
<string name="toast_habit_archived">Thói quen đã được lưu trữ</string>
<string name="toast_habit_unarchived">Thói quen đã bị huỷ lưu trữ</string>
<string name="overview">Tổng quan</string>
<string name="habit_strength">Độ mạnh của thói quen</string>
<string name="history">Lịch sử</string>
<string name="clear">Dọn sạch</string>
<string name="description_hint">Câu hỏi (Bạn đã ... hôm nay?)</string>
<string name="repeat">Lặp lại</string>
<string name="times_every">times in</string>
<string name="days">ngày</string>
<string name="reminder">Nhắc nhở</string>
<string name="discard">Loại bỏ</string>
<string name="save">Lưu</string>
<string name="streaks">Mức độ</string>
<string name="no_habits_found">Bạn có không có thói quen nào đang hoạt động</string>
<string name="long_press_to_toggle">Nhấn giữ để đánh dấu hoặc bỏ đánh dấu</string>
<string name="reminder_off">Tắt</string>
<string name="validation_name_should_not_be_blank">Tên không thể để trống.</string>
<string name="validation_number_should_be_positive">Số phải là số dương.</string>
<string name="validation_at_most_one_rep_per_day">Bạn có thể có tối đa một lặp lại mỗi ngày</string>
<string name="create_habit">Tạo thói quen</string>
<string name="edit_habit">Chỉnh sửa thói quen</string>
<string name="check">Kiểm tra</string>
<string name="snooze">Lúc khác</string>
<!-- App introduction -->
<string name="intro_title_1">Chào mừng</string>
<string name="intro_description_1">Theo dõi thói quen Loop giúp bạn tạo ra và duy trì những thói quen tốt.</string>
<string name="intro_title_2">Tạo một số thói quen mới</string>
<string name="intro_description_2">Mỗi ngày, sau khi thực hiện các thói quen của bạn, hãy đánh dấu vào ứng dụng.</string>
<string name="intro_title_3">Hãy duy trì thói quen</string>
<string name="intro_description_3">Thói quen thực hiện một cách nhất quán trong một thời gian dài sẽ kiếm được trọn vẹn một ngôi sao.</string>
<string name="intro_title_4">Theo dõi quá trình của bạn</string>
<string name="intro_description_4">Đồ thị chi tiết cho bạn thấy các thói quen của bạn được cải thiện như thế nào theo thời gian.</string>
<string name="interval_15_minutes">15 phút</string>
<string name="interval_30_minutes">30 phút</string>
<string name="interval_1_hour">1 giờ</string>
<string name="interval_2_hour">2 giờ</string>
<string name="interval_4_hour">4 giờ</string>
<string name="interval_8_hour">8 giờ</string>
<string name="interval_24_hour">24 giờ</string>
<string name="pref_toggle_title">Bấm nhanh để chuyển trạng thái</string>
<string name="pref_toggle_description">Chỉ cần chạm một lần để đánh dấu thay cho việc nhấn giữ. Tiện lợi hơn nhưng có thể đánh dấu sai.</string>
<string name="pref_snooze_interval_title">Khoảng thời gian báo lại lời nhắc</string>
<string name="pref_rate_this_app">Đánh giá ứng dụng trên Google Play</string>
<string name="pref_send_feedback">Gửi phản hồi cho nhà phát triển</string>
<string name="pref_view_source_code">Xem mã nguồn trên Github</string>
<string name="pref_view_app_introduction">Xem giới thiệu ứng dụng</string>
<string name="links">Liên kết</string>
<string name="behavior">Hành vi</string>
<string name="name">Tên</string>
<string name="settings">Cài đặt</string>
<string name="snooze_interval">Khoảng thời gian tạm dừng</string>
<string name="hint_title">Bạn đã biết?</string>
<string name="hint_drag">Để sắp xếp lại các mục, nhấn giữ tên thói quen, sau đó kéo tới vị trí chính xác.</string>
<string name="hint_landscape">Bạn có thể xem thêm ngày bằng cách đặt điện thoại ở chế độ ngang.</string>
<string name="delete_habits">Xoá bỏ thói quen</string>
<string name="delete_habits_message">Thói quen sẽ bị xoá vĩnh viễn. Hành động này không thể khôi phục.</string>
<string name="habit_not_found">Thói quen đã bị xoá hoặc không tìm thấy</string>
<string name="weekends">Cuối tuần</string>
<string name="any_weekday">Thứ 2 đến thứ 6</string>
<string name="any_day">Bất kỳ ngày nào trong tuần</string>
<string name="select_weekdays">Chọn ngày</string>
<string name="export_to_csv">Xuất dưới dạng CSV</string>
<string name="done_label">Xong</string>
<string name="clear_label">Dọn sạch</string>
<string name="select_hours">Chọn giờ</string>
<string name="select_minutes">Chọn phút</string>
<string name="about">Giới thiệu</string>
<string name="translators">Dịch giả</string>
<string name="developers">Nhà phát triển</string>
<string name="version_n">Phiên bản %s</string>
<string name="frequency">Tần suất</string>
<string name="checkmark">Đánh dấu</string>
<string name="strength">Độ mạnh</string>
<string name="best_streaks">Duy trì lâu nhất</string>
<string name="current_streaks">Số ngày duy trì hiện tại</string>
<string name="number_of_repetitions">Số lần lặp</string>
<string name="last_x_days">Đã thực hiện được %d ngày</string>
<string name="last_x_weeks">Đã thực hiện được %d tuần</string>
<string name="last_x_months">Đã thực hiện được %d tháng</string>
<string name="last_x_years">Đã thực hiện được %d năm</string>
<string name="all_time">Toàn bộ thời gian</string>
<string name="every_day">Hàng ngày</string>
<string name="every_week">Hàng tuần</string>
<string name="two_times_per_week">2 lần một tuần</string>
<string name="five_times_per_week">5 lần một tuần</string>
<string name="custom_frequency">Tuỳ chỉnh&#8230;</string>
<string name="help">Trợ giúp &amp; Câu hỏi</string>
<string name="could_not_export">Xuất dữ liệu thất bại.</string>
<string name="could_not_import">Nhập dữ liệu thất bại.</string>
<string name="file_not_recognized">Không xác nhận được file.</string>
<string name="habits_imported">Thói quen được nhập thành công.</string>
<string name="full_backup_success">Xuất bản sao lưu đầy đủ thành công.</string>
<string name="import_data">Nhập dữ liệu</string>
<string name="export_full_backup">Xuất toàn bộ sao lưu</string>
<string name="import_data_summary">Hồ trợ các bản sao lưu đầy đủ được xuất ra bởi ứng dụng, cũng như các file được tạo bởi Tickmate, HabitBull hoặc Rewire. Xem FAQ để biết thêm thông tin.</string>
<string name="export_as_csv_summary">Các file tạo ra có thể mở bằng các phần mềm bảng tĩnh như Microsoft Excel hoặc OpenOffice Calc. Nhưng file này không thể nhập lại.</string>
<string name="export_full_backup_summary">Tạo ra một tệp chứa tất cả dữ liệu của bạn. Tệp này có thể nhập lại.</string>
<string name="bug_report_failed">Tạo báo cáo về lỗi.</string>
<string name="generate_bug_report">Tạo báo cáo lỗi</string>
<string name="troubleshooting">Xử lí sự cố</string>
<string name="help_translate">Giúp dịch ứng dụng</string>
<string name="night_mode">Chế độ ban đêm</string>
<string name="use_pure_black">Sử dụng màu đen thuần trong chế độ ban đêm</string>
<string name="pure_black_description">Thay thế nền màu xám bởi màu đen thuần trong chế độ ban đêm. Giảm thiểu việc sử dụng pin của điện thoại có màn hình AMOLED.</string>
<string name="interface_preferences">Giao diện</string>
<string name="reverse_days">Đảo ngược thứ tự của ngày</string>
<string name="reverse_days_description">Hiển thị ngày ngược trên màn hình chính</string>
<string name="day">Ngày</string>
<string name="week">Tuần</string>
<string name="month">Tháng</string>
<string name="quarter">Quý</string>
<string name="year">Năm</string>
<string name="total">Tổng</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">lần trong</string>
<string name="every_x_days">Mỗi %d ngày</string>
<string name="every_x_weeks">Mỗi %d tuần</string>
<string name="every_x_months">Mỗi %d tháng</string>
<string name="score">Điểm</string>
<string name="reminder_sound">Âm báo</string>
<string name="none">Không có</string>
<string name="filter">Lọc</string>
<string name="hide_completed">Ẩn mục đã hoàn thành</string>
<string name="hide_archived">Ẩn mục đã lưu trữ</string>
<string name="sticky_notifications">Gửi thông báo cố định</string>
<string name="sticky_notifications_description">Không cho các thông báo bị vuốt ngang mất.</string>
<string name="repair_database">Sửa cơ sở dữ liệu</string>
<string name="database_repaired">Cơ sở dữ liệu đã được sửa.</string>
<string name="uncheck">Bỏ đánh dấu</string>
<string name="toggle">Bật/tắt</string>
<string name="action">Hành động</string>
<string name="habit">Thói quen</string>
<string name="sort">Sắp xếp</string>
<string name="manually">Thủ công</string>
<string name="by_name">Theo tên</string>
<string name="by_color">Theo màu sắc</string>
<string name="by_score">Theo điểm số</string>
<string name="download">Tải về</string>
<string name="export">Xuất dữ liệu ra</string>
</resources>

View File

@@ -46,7 +46,7 @@
<string name="times_every">次每</string>
<string name="days"></string>
<string name="reminder">提醒</string>
<string name="discard">放棄</string>
<string name="discard">取消</string>
<string name="save">保存</string>
<string name="streaks">記錄</string>
<string name="no_habits_found">你還沒有任何習慣</string>
@@ -74,6 +74,7 @@
<string name="interval_2_hour">2小時</string>
<string name="interval_4_hour">4小時</string>
<string name="interval_8_hour">8小時</string>
<string name="interval_24_hour">24 小時</string>
<string name="pref_toggle_title">短按來記錄習慣</string>
<string name="pref_toggle_description">但有可能造成意外記錄</string>
<string name="pref_snooze_interval_title">提醒延遲間隔</string>
@@ -91,6 +92,7 @@
<string name="hint_landscape">把手機橫放可以看到更多天數</string>
<string name="delete_habits">刪除</string>
<string name="delete_habits_message">習慣會被永久刪除,而且該操作不可恢復</string>
<string name="habit_not_found">習慣已刪除/找不到</string>
<string name="weekends">周末</string>
<string name="any_weekday">工作日</string>
<string name="any_day">每天</string>
@@ -120,7 +122,7 @@
<string name="two_times_per_week">每週兩次</string>
<string name="five_times_per_week">每週五次</string>
<string name="custom_frequency">自訂週期</string>
<string name="help">幫助&amp;amp;常見問題</string>
<string name="help">幫助&amp; 常見問題</string>
<string name="could_not_export">輸出資料失敗</string>
<string name="could_not_import">輸入資料失敗</string>
<string name="file_not_recognized">無法辨識</string>
@@ -146,6 +148,7 @@
<string name="month"></string>
<string name="quarter"></string>
<string name="year"></string>
<string name="total">總計</string>
<!-- Middle part of the sentence '1 time in xx days' -->
<string name="time_every">次每</string>
<string name="every_x_days">每 %d 天</string>
@@ -154,4 +157,22 @@
<string name="score">分數</string>
<string name="reminder_sound">提醒音效</string>
<string name="none"></string>
<string name="filter">篩選</string>
<string name="hide_completed">隱藏已完成的習慣</string>
<string name="hide_archived">隱藏已封存習慣</string>
<string name="sticky_notifications">使提醒保持常駐</string>
<string name="sticky_notifications_description">防止提醒被滑動移除</string>
<string name="repair_database">修復資料庫</string>
<string name="database_repaired">資料庫已修復</string>
<string name="uncheck">取消選取</string>
<string name="toggle">切換</string>
<string name="action">操作</string>
<string name="habit">習慣</string>
<string name="sort">排序</string>
<string name="manually">手動</string>
<string name="by_name">根據名稱</string>
<string name="by_color">根據顏色</string>
<string name="by_score">根據分數</string>
<string name="download">下載</string>
<string name="export">匯出</string>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#1976D2</color>
</resources>

View File

@@ -204,4 +204,6 @@
<string name="by_score">By score</string>
<string name="download">Download</string>
<string name="export">Export</string>
<string name="customize_notification_summary">Change sound, vibration, light and other notification settings</string>
<string name="customize_notification">Customize notifications</string>
</resources>

View File

@@ -68,6 +68,11 @@
android:title="@string/sticky_notifications"
android:summary="@string/sticky_notifications_description"/>
<Preference
android:key="reminderCustomize"
android:summary="@string/customize_notification_summary"
android:title="@string/customize_notification"/>
</PreferenceCategory>
<PreferenceCategory

View File

@@ -58,4 +58,11 @@ public class BaseUnitTest
cal.set(year, month, day);
return cal.getTimeInMillis();
}
public long timestamp(int year, int month, int day, int hourOfDay, int minute, int second)
{
GregorianCalendar cal = DateUtils.getStartOfTodayCalendar();
cal.set(year, month, day, hourOfDay, minute, second);
return cal.getTimeInMillis();
}
}

View File

@@ -65,7 +65,7 @@ public class ReminderControllerTest extends BaseUnitTest
{
Habit habit = mock(Habit.class);
long now = timestamp(2015, 1, 1);
DateUtils.setFixedLocalTime(now);
DateUtils.setFixedLocalTime(DateUtils.removeTimezone(now));
when(preferences.getSnoozeInterval()).thenReturn(15L);
controller.onSnooze(habit);

Some files were not shown because too many files have changed in this diff Show More