+
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3 of the License, or (at your option) any later
+ version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program. If not, see .
+ */
+
+package com.ichi2.utils;
+
+
+import org.apache.commons.collections4.list.SetUniqueList;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.TreeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+
+/**
+ * A collection of items that doesn't allow duplicate items, and allows fast random access, lookup, maintaining order, and sorting.
+ *
+ * The {@link List} interface makes certain assumptions/requirements. This
+ * implementation breaks these in certain ways, but this is merely the result of
+ * rejecting duplicates. Each violation is explained in the method, but it
+ * should not affect you.
+ *
+ * This class does also implement the {@link Set} interface, and as a result
+ * you should bear in mind that Sets require immutable objects to function correctly.
+ *
+ * @implNote The implementation of this class extends {@link SetUniqueList} and adds the ability to define a comparator
+ * to be used to judge uniqueness of elements, and allows sorting.
+ *
+ * Data structures used internally:
+ * - {@link ArrayList} to enable fast random access, sorting, and maintaining order of items during iteration
+ * - {@link TreeSet} if a comparator is given or a {@link HashSet} otherwise.
+ */
+public class UniqueArrayList extends SetUniqueList implements List, Set {
+
+ /**
+ * Internal list used in {@link SetUniqueList} implementation.
+ *
+ * This is the same list as the one used internally in {@link SetUniqueList}. We keep a reference to it here in
+ * order to be able to sort it. {@link SetUniqueList} implementation needs to make sure the internal {@link Set}
+ * and {@link List} don't get out of sync, and {@link SetUniqueList} cannot be sorted via {@link Collections#sort(List)}
+ * or {@link SetUniqueList#sort(Comparator)} both will throw an exception, due to a limitation on this class {@link java.util.ListIterator}
+ *
+ * Sorting can be only done via {@link UniqueArrayList#sort()} or {@link UniqueArrayList#sort(Comparator)}.
+ *
+ * Modification to this list reference should be done with cautious to avoid having the internal {@link Set} out of sync
+ */
+ private final List mList;
+
+
+ /**
+ * Constructor that wraps (not copies) the List and specifies the set to use.
+ *
+ * The set and list must both be correctly initialised to the same elements.
+ *
+ * @param set the set to decorate, must not be null
+ * @param list the list to decorate, must not be null
+ * @throws NullPointerException if set or list is null
+ */
+ protected UniqueArrayList(final List list, final Set set) {
+ super(list, set);
+ mList = list;
+ }
+
+
+ /**
+ * Constructs a new empty {@link UniqueArrayList}
+ */
+ public UniqueArrayList() {
+ this(new ArrayList<>(), new HashSet<>());
+ }
+
+
+ /**
+ * Constructs a new {@link UniqueArrayList} containing the elements of the specified collection
+ *
+ * @param source the source collection that will be used to construct UniqueArrayList
+ */
+ public static UniqueArrayList from(final List source) {
+ return UniqueArrayList.from(source, null);
+ }
+
+ /**
+ * Constructs a new {@link UniqueArrayList} containing the elements of the specified collection, with an optional
+ * comparator to be used to judge uniqueness.
+ *
+ * @implNote Modified implantation of {@link SetUniqueList#setUniqueList(List)} to :
+ * - support using comparators to check for uniqueness.
+ * - make a copy of the list passed.
+ *
+ * @param source the source collection that will be used to construct UniqueArrayList
+ * @param comparator used to judge uniqueness
+ */
+ public static UniqueArrayList from(@NonNull List source, @Nullable Comparator super E> comparator) {
+ if (source == null) {
+ throw new NullPointerException("List must not be null");
+ }
+
+ Set set;
+ if (comparator == null) {
+ set = new HashSet<>();
+ } else {
+ set = new TreeSet<>(comparator);
+ }
+
+ final UniqueArrayList sl = new UniqueArrayList<>(new ArrayList<>(), set);
+
+ sl.addAll(source);
+
+ return sl;
+ }
+
+
+ /**
+ * Sorts the list into ascending order, according to the
+ * {@linkplain Comparable natural ordering} of its elements.
+ * All elements in the list must implement the {@link Comparable}
+ * interface. Furthermore, all elements in the list must be
+ * mutually comparable (that is, {@code e1.compareTo(e2)}
+ * must not throw a {@code ClassCastException} for any elements
+ * {@code e1} and {@code e2} in the list).
+ *
+ * @see #sort(Comparator)
+ */
+ public void sort() {
+ sort(null);
+ }
+
+ /**
+ * Sorts the list according to the order induced by the specified comparator.
+ * All elements in the list must be mutually comparable using the
+ * specified comparator (that is, {@code c.compare(e1, e2)} must not throw
+ * a {@code ClassCastException} for any elements {@code e1} and {@code e2}
+ * in the list).
+ *
+ * This sort is guaranteed to be stable: equal elements will
+ * not be reordered as a result of the sort.
+ *
+ *
The specified list must be modifiable, but need not be resizable.
+ *
+ * @implNote
+ * This implementation defers to the {@link Collections#sort(List, Comparator)}
+ * method using this list and the given comparator.
+ *
+ * DO NOT call {@link Collections#sort(List, Comparator)} using this list directly
+ * this will throw due to a limitation on setting items on the {@link ListIterator}
+ * returned by {@link #listIterator()}
+ *
+ * @param c the comparator to determine the order of the list. A
+ * {@code null} value indicates that the elements' natural
+ * ordering should be used.
+ *
+ * @see Collections#sort(List, Comparator)
+ */
+ @Override
+ public void sort(@Nullable Comparator super E> c) {
+ Collections.sort(mList, c);
+ }
+
+
+ @NonNull
+ @Override
+ @RequiresApi(24)
+ public Spliterator spliterator() {
+ return super.spliterator();
+ }
+}
diff --git a/AnkiDroid/src/test/java/com/ichi2/utils/UniqueArrayListTest.java b/AnkiDroid/src/test/java/com/ichi2/utils/UniqueArrayListTest.java
new file mode 100644
index 000000000000..7df32441f759
--- /dev/null
+++ b/AnkiDroid/src/test/java/com/ichi2/utils/UniqueArrayListTest.java
@@ -0,0 +1,570 @@
+/*
+ Copyright (c) 2021 Tarek Mohamed Abdalla
+
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3 of the License, or (at your option) any later
+ version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program. If not, see .
+ */
+
+package com.ichi2.utils;
+
+import org.hamcrest.collection.IsIterableContainingInOrder;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+public class UniqueArrayListTest {
+
+
+ private List dupData = Arrays.asList(
+ "55",
+ "TEst",
+ "TEst",
+ "12",
+ "TEst",
+ "dsf23A",
+ "dsf23A",
+ "dsf23A",
+ "dsf23A",
+ "23",
+ "12",
+ "sd",
+ "TEst",
+ "55"
+ );
+
+ private List noDupData = Arrays.asList(
+ "55",
+ "TEst",
+ "12",
+ "dsf23A",
+ "23",
+ "sd"
+ );
+
+ private void assertSameLists(List a, List b){
+ assertThat(b, IsIterableContainingInOrder.contains(a.toArray()));
+ }
+
+ private void assertNotSameLists(List a, List b){
+ assertThat(b, not(IsIterableContainingInOrder.contains(a.toArray())));
+ }
+
+ @Test
+ public void testOrderIsMaintained() {
+ List longs = Arrays.asList(1, 1, 2, 3, 4, 1, 5, 1, 6, 7, 8, 9, 10, 11, 1, 12, 13);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertTrue(uniqueList.indexOf(5) > uniqueList.indexOf(1));
+ }
+
+
+ @Test
+ public void test_Sorting() {
+ List longs = Arrays.asList(10, 9, 7, 3, 2, -1, 5, 1, 65, -656);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+ Collections.sort(longs);
+ uniqueList.sort();
+ assertSameLists(longs, uniqueList);
+ }
+
+
+ @Test
+ public void test_uniqueness_after_sorting() {
+ List longs = Arrays.asList(10, 9, 7, 3, 2, -1, 5, 1, 65, -656);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+ Collections.sort(longs);
+ uniqueList.sort();
+ assertSameLists(longs, uniqueList);
+
+ uniqueList.addAll(longs);
+ uniqueList.add(10);
+ uniqueList.add(5, 65);
+
+ assertSameLists(longs, uniqueList);
+
+ uniqueList.add(575757);
+ assertNotSameLists(longs, uniqueList);
+ }
+
+
+ @Test
+ public void test_comparator() {
+ List list = Arrays.asList("TarekkMA", "TarekkMA", "TarekkmA", "tarekkma");
+ UniqueArrayList uniqueList = UniqueArrayList.from(list, String.CASE_INSENSITIVE_ORDER);
+
+ assertEquals(1, uniqueList.size());
+ assertEquals("TarekkMA", uniqueList.get(0));
+
+ uniqueList.clear();
+ Collections.reverse(list);
+ uniqueList.addAll(list);
+
+ assertEquals(1, uniqueList.size());
+ assertEquals("tarekkma", uniqueList.get(0));
+ }
+
+
+ @Test
+ public void test_add_unique_after_sorting() {
+ List longs = Arrays.asList(10, 9, 7, 3, 2, -1, 5, 1, 65, -656);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+ Collections.sort(longs);
+ uniqueList.sort();
+ assertEquals(longs, uniqueList);
+ uniqueList.addAll(longs);
+ assertEquals(longs, uniqueList);
+ }
+
+
+ @Test
+ public void testFromCollection() {
+ UniqueArrayList uniqueArrayList = UniqueArrayList.from(dupData);
+ assertEquals(noDupData, uniqueArrayList);
+
+ uniqueArrayList = new UniqueArrayList<>();
+ assertTrue(uniqueArrayList.isEmpty());
+ }
+
+
+ @Test
+ public void testFromEmptyCollection() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+ assertTrue(uniqueArrayList.isEmpty());
+ }
+
+
+ @Test
+ public void test_add_not_existing() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+ assertEquals(Arrays.asList("a", "Z", "f"), uniqueArrayList);
+ }
+
+
+ @Test
+ public void test_add_existing() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+ assertEquals(Arrays.asList("a", "Z", "f"), uniqueArrayList);
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+ assertEquals(Arrays.asList("a", "Z", "f"), uniqueArrayList);
+ }
+
+
+ @Test
+ public void test_set_not_existing() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+
+ String res = uniqueArrayList.set(1, "m");
+
+ assertEquals(Arrays.asList("a", "m", "f"), uniqueArrayList);
+ assertEquals("Z", res);
+ }
+
+
+ @Test
+ public void test_set_existing() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+
+ String res = uniqueArrayList.set(1, "a");
+
+ assertEquals(Arrays.asList("a", "f"), uniqueArrayList);
+ assertEquals("Z", res);
+ }
+
+ @Test
+ public void test_set_will_remove_replaced_item() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.set(0, "b");
+ uniqueArrayList.add("a");
+
+ assertSameLists(Arrays.asList("b", "a"), uniqueArrayList);
+ }
+
+
+ @Test
+ public void test_addAll_no_change() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+ boolean res = uniqueArrayList.addAll(Arrays.asList("a", "Z", "f"));
+
+ assertEquals(Arrays.asList("a", "Z", "f"), uniqueArrayList);
+ assertFalse(res);
+ }
+
+
+ @Test
+ public void test_addAll_full_change() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+ boolean res = uniqueArrayList.addAll(Arrays.asList("w", "x", "y"));
+
+ assertEquals(Arrays.asList("a", "Z", "f", "w", "x", "y"), uniqueArrayList);
+ assertTrue(res);
+ }
+
+
+ @Test
+ public void test_addAll_partial_change() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+ boolean res = uniqueArrayList.addAll(Arrays.asList("f", "Y", "Z"));
+
+ assertEquals(Arrays.asList("a", "Z", "f", "Y"), uniqueArrayList);
+ assertTrue(res);
+ }
+
+
+ @Test
+ public void test_addAll_withIndex_no_change() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+ boolean res = uniqueArrayList.addAll(1, Arrays.asList("a", "Z", "f"));
+
+ assertEquals(Arrays.asList("a", "Z", "f"), uniqueArrayList);
+ assertFalse(res);
+ }
+
+
+ @Test
+ public void test_addAll_withIndex_full_change() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+ boolean res = uniqueArrayList.addAll(1, Arrays.asList("w", "x", "y"));
+
+ assertSameLists(Arrays.asList("a", "w", "x", "y", "Z", "f"), uniqueArrayList);
+ assertTrue(res);
+ }
+
+
+ @Test
+ public void test_addAll_withIndex_partial_change() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+ boolean res = uniqueArrayList.addAll(1, Arrays.asList("f", "Y", "Z"));
+
+ assertSameLists(Arrays.asList("a", "Y", "Z", "f"), uniqueArrayList);
+ assertTrue(res);
+ }
+
+
+ @Test
+ public void test_addAll_withIndex_last_position() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ uniqueArrayList.add("a");
+ uniqueArrayList.add("Z");
+ uniqueArrayList.add("f");
+
+ boolean res = uniqueArrayList.addAll(uniqueArrayList.size(), Arrays.asList("w", "x", "y"));
+
+ assertSameLists(Arrays.asList("a", "Z", "f", "w", "x", "y"), uniqueArrayList);
+ assertTrue(res);
+ }
+
+
+ @Test
+ public void test_addAll_order_1() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ final List l1 = Arrays.asList(1, 2, 3, 4, 5);
+ assertTrue(uniqueArrayList.addAll(l1));
+ assertSameLists(l1, uniqueArrayList);
+
+ final List l2 = Arrays.asList(5, 4, 3, 2, 1, 0);
+ assertTrue(uniqueArrayList.addAll(0,l2));
+
+ final List l3 = Arrays.asList(0, 1, 2, 3, 4, 5);
+ assertSameLists(l3, uniqueArrayList);
+ }
+
+
+ @Test
+ public void test_addAll_order_2() {
+ UniqueArrayList uniqueArrayList = new UniqueArrayList<>();
+
+ final List l1 = Arrays.asList(1, 2, 3, 4, 5);
+ assertTrue(uniqueArrayList.addAll(l1));
+ assertSameLists(l1, uniqueArrayList);
+
+ final List l2 = Arrays.asList(0, 1, 2, 3, 4, 5);
+ assertTrue(uniqueArrayList.addAll(0,l2));
+
+ assertSameLists(l2, uniqueArrayList);
+ }
+
+
+ @Test
+ public void test_clear() {
+ List longs = Arrays.asList(1, 1, 2, 3, 4, 1, 5, 1, 6, 7, 8, 9, 10, 11, 1, 12, 13);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertFalse(uniqueList.isEmpty());
+ uniqueList.clear();
+ assertTrue(uniqueList.isEmpty());
+ uniqueList.addAll(longs);
+ assertFalse(uniqueList.isEmpty());
+ }
+
+
+ @Test
+ public void test_remove_object() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertNotEquals(-1, uniqueList.indexOf(1L));
+ uniqueList.remove(1L);
+ assertEquals(-1, uniqueList.indexOf(1L));
+ uniqueList.set(10, 1L);
+ assertEquals(10, uniqueList.indexOf(1L));
+ }
+
+
+ @Test
+ public void test_remove_index() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertNotEquals(-1, uniqueList.indexOf(1L));
+ uniqueList.remove(0/*index of 1L*/);
+ assertEquals(-1, uniqueList.indexOf(1L));
+ uniqueList.set(10, 1L);
+ assertEquals(10, uniqueList.indexOf(1L));
+ }
+
+
+ @Test
+ public void test_contain() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertTrue(uniqueList.contains(1L));
+ assertFalse(uniqueList.contains(1502L));
+ }
+
+
+ @Test
+ public void test_get_by_index() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertEquals((Long) 1L, uniqueList.get(0));
+ assertNotEquals((Long) 1L, uniqueList.get(1));
+
+
+ assertEquals((Long) 13L, uniqueList.get(12));
+ assertEquals((Long) 12L, uniqueList.get(11));
+ }
+
+
+ @Test
+ public void test_indexOf_and_lastIndexOf() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertEquals(0, uniqueList.indexOf(1L));
+ assertEquals(5, uniqueList.indexOf(6L));
+ assertEquals(12, uniqueList.indexOf(13L));
+ assertEquals(9, uniqueList.indexOf(10L));
+ assertEquals(2, uniqueList.indexOf(3L));
+
+ assertEquals(0, uniqueList.lastIndexOf(1L));
+ assertEquals(5, uniqueList.lastIndexOf(6L));
+ assertEquals(12, uniqueList.lastIndexOf(13L));
+ assertEquals(9, uniqueList.lastIndexOf(10L));
+ assertEquals(2, uniqueList.lastIndexOf(3L));
+ }
+
+
+ @Test
+ public void test_iterator() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ List list = new ArrayList<>();
+ uniqueList.iterator().forEachRemaining(list::add);
+
+ assertEquals(Arrays.asList(
+ 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L
+ ), list);
+ }
+
+
+ @Test
+ public void test_listIterator() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ List list = new ArrayList<>();
+ uniqueList.listIterator().forEachRemaining(list::add);
+
+ assertEquals(Arrays.asList(
+ 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L
+ ), list);
+ }
+
+
+ @Test
+ public void test_listIterator_index() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ List list = new ArrayList<>();
+ uniqueList.listIterator(5).forEachRemaining(list::add);
+
+ assertEquals(Arrays.asList(
+ 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L
+ ), list);
+ }
+
+
+ @Test
+ public void test_subList() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertEquals(Arrays.asList(
+ 6L, 7L, 8L, 9L, 10L
+ ), uniqueList.subList(5, 10));
+ }
+
+
+ @Test
+ public void test_containsAll() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertTrue(uniqueList.containsAll(Arrays.asList(1L, 10L, 13L)));
+ assertFalse(uniqueList.containsAll(Arrays.asList(1L, 130L, 13L)));
+ assertFalse(uniqueList.containsAll(Arrays.asList(-1L, 130L, 1003L)));
+ }
+
+
+ @Test
+ public void test_retainAll() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertNotEquals(2, uniqueList.size());
+ uniqueList.retainAll(Arrays.asList(1L, 3L));
+ assertEquals(2, uniqueList.size());
+ assertEquals(Arrays.asList(1L, 3L), uniqueList);
+ }
+
+
+ @Test
+ public void test_isEmpty() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ assertFalse(uniqueList.isEmpty());
+ uniqueList.removeAll(Arrays.asList(
+ 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L
+ ));
+ assertTrue(uniqueList.isEmpty());
+ }
+
+
+ @Test
+ public void test_toArray() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ final Object[] arr = Arrays.asList(
+ 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L
+ ).toArray();
+
+ assertArrayEquals(arr, uniqueList.toArray());
+ }
+
+
+ @Test
+ public void test_toArrayType() {
+ List longs = Arrays.asList(1L, 1L, 2L, 3L, 4L, 1L, 5L, 1L, 6L, 7L, 8L, 9L, 10L, 11L, 1L, 12L, 13L);
+ UniqueArrayList uniqueList = UniqueArrayList.from(longs);
+
+ final Long[] arr = Arrays.asList(
+ 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L
+ ).toArray(new Long[] {});
+
+ final Long[] arr_res = uniqueList.toArray(new Long[] {});
+ assertArrayEquals(arr, arr_res);
+
+ assertThat(arr_res[0], instanceOf(Long.class));
+ }
+}
\ No newline at end of file
From dca44e9ed5dfd1abe28ceb01ce2c467869d6e6dc Mon Sep 17 00:00:00 2001
From: AnkiDroid Translations
Date: Wed, 31 Mar 2021 20:24:30 +0000
Subject: [PATCH 018/171] Updated strings from Crowdin
---
.../src/main/res/values-ar/10-preferences.xml | 2 +-
.../src/main/res/values-de/02-strings.xml | 6 +-
.../src/main/res/values-de/03-dialogs.xml | 4 +-
.../src/main/res/values-de/10-preferences.xml | 4 +-
.../src/main/res/values-eo/02-strings.xml | 4 +-
.../src/main/res/values-eo/03-dialogs.xml | 4 +-
.../src/main/res/values-eo/10-preferences.xml | 4 +-
.../src/main/res/values-es-rAR/01-core.xml | 2 +-
.../src/main/res/values-es-rAR/02-strings.xml | 4 +-
.../src/main/res/values-es-rES/01-core.xml | 2 +-
.../src/main/res/values-es-rES/02-strings.xml | 4 +-
AnkiDroid/src/main/res/values-fa/01-core.xml | 16 +++---
.../src/main/res/values-fa/02-strings.xml | 44 +++++++--------
.../src/main/res/values-fa/03-dialogs.xml | 56 +++++++++----------
.../src/main/res/values-fa/04-network.xml | 2 +-
.../res/values-fa/16-multimedia-editor.xml | 2 +-
AnkiDroid/src/main/res/values-fr/01-core.xml | 2 +-
.../src/main/res/values-fr/02-strings.xml | 4 +-
.../src/main/res/values-fr/10-preferences.xml | 4 +-
.../src/main/res/values-hi/03-dialogs.xml | 2 +-
.../src/main/res/values-hi/07-cardbrowser.xml | 12 ++--
.../src/main/res/values-hi/10-preferences.xml | 2 +-
AnkiDroid/src/main/res/values-it/01-core.xml | 4 +-
.../src/main/res/values-it/02-strings.xml | 6 +-
.../src/main/res/values-it/03-dialogs.xml | 6 +-
.../src/main/res/values-it/04-network.xml | 2 +-
.../src/main/res/values-it/10-preferences.xml | 4 +-
.../main/res/values-it/18-standard-models.xml | 4 +-
.../src/main/res/values-or/02-strings.xml | 2 +-
.../src/main/res/values-or/03-dialogs.xml | 9 ++-
.../src/main/res/values-or/04-network.xml | 4 +-
.../src/main/res/values-or/09-backup.xml | 2 +-
.../src/main/res/values-or/10-preferences.xml | 4 +-
.../main/res/values-or/17-model-manager.xml | 2 +-
.../src/main/res/values-pl/02-strings.xml | 4 +-
.../src/main/res/values-pl/03-dialogs.xml | 2 +-
.../src/main/res/values-pl/10-preferences.xml | 2 +-
AnkiDroid/src/main/res/values-sv/01-core.xml | 50 ++++++++---------
.../src/main/res/values-tt/02-strings.xml | 2 +-
.../src/main/res/values-tt/03-dialogs.xml | 4 +-
.../src/main/res/values-tt/04-network.xml | 2 +-
.../src/main/res/values-uk/02-strings.xml | 4 +-
.../src/main/res/values-uk/03-dialogs.xml | 4 +-
.../src/main/res/values-uk/10-preferences.xml | 2 +-
.../main/res/values-zh-rCN/10-preferences.xml | 2 +-
.../marketdescription-ar.txt | 2 +-
.../marketdescription-fr.txt | 4 +-
.../marketdescription-it.txt | 2 +-
48 files changed, 160 insertions(+), 161 deletions(-)
diff --git a/AnkiDroid/src/main/res/values-ar/10-preferences.xml b/AnkiDroid/src/main/res/values-ar/10-preferences.xml
index d145b60a3061..372ab5a89f97 100644
--- a/AnkiDroid/src/main/res/values-ar/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-ar/10-preferences.xml
@@ -173,7 +173,7 @@
مهلة مربع الوقت
XXX دقيقة
طريقة المعالجة الجديدة للمناطق الزمنية
- Start of next day (0 is midnight, 23 is 11PM)
+ بداية اليوم التالي (0 يعني منتصف الليل، 23 يعني الساعة 11 مساءً)
XXX ساعة بعد منتصف الليل
مجدول V2 التجريبي
تفعيل المجدول التجريبي. يفرض التغييرات في جانب واحد عند المزامنة التالية
diff --git a/AnkiDroid/src/main/res/values-de/02-strings.xml b/AnkiDroid/src/main/res/values-de/02-strings.xml
index b86ae7db97b9..23ccf9fcbaf0 100644
--- a/AnkiDroid/src/main/res/values-de/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-de/02-strings.xml
@@ -313,7 +313,7 @@
Fehler im Karteninhalt: Laden von »%s« fehlgeschlagen
Fehler beim Karteninhalt: Medienaktualisierung erforderlich
- Loading http resources is no longer supported
+ Das Laden von http-Ressourcen wird nicht mehr unterstützt
Fehler beim Laden von Stapel »%s«
Stapel durchsuchen
@@ -345,6 +345,6 @@
+
-
- Email address is required
- Password is required
+ Die E-Mail-Adresse ist erforderlich
+ Das Passwort ist erforderlich
diff --git a/AnkiDroid/src/main/res/values-de/03-dialogs.xml b/AnkiDroid/src/main/res/values-de/03-dialogs.xml
index cd649a875b4c..ad00444da41f 100644
--- a/AnkiDroid/src/main/res/values-de/03-dialogs.xml
+++ b/AnkiDroid/src/main/res/values-de/03-dialogs.xml
@@ -228,8 +228,8 @@
Discord
Facebook
Twitter
- Send troubleshooting report
- Report already submitted
+ Fehlerbehebungsbericht senden
+ Bericht bereits eingereicht
- Ein automatische Synchronisierung kann in %d Sekunden ausgelöst werden
diff --git a/AnkiDroid/src/main/res/values-de/10-preferences.xml b/AnkiDroid/src/main/res/values-de/10-preferences.xml
index cc8e3052ba2b..acf0d1a22faf 100644
--- a/AnkiDroid/src/main/res/values-de/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-de/10-preferences.xml
@@ -173,7 +173,7 @@
Zeitlimitwert
XXX min
Umgang mit neuer Zeitzone
- Start of next day (0 is midnight, 23 is 11PM)
+ Start des nächsten Tages (0 ist Mitternacht, 23 ist 23 Uhr)
XXX Stunden nach Mitternacht
Experimenteller Zeitplaner V2
Experimentellen Zeitplaner aktivieren. Erzwingt bei der nächsten Synchronisation Änderungen in einer Richtung
@@ -290,5 +290,5 @@
Einfache Formatierung von Antworten
Behebt das Erscheinen von »« in eingetippten Antworten
- Open Changelog
+ Änderungsprotokoll öffnen
diff --git a/AnkiDroid/src/main/res/values-eo/02-strings.xml b/AnkiDroid/src/main/res/values-eo/02-strings.xml
index 00b51ce7d6f7..92047385eff4 100644
--- a/AnkiDroid/src/main/res/values-eo/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-eo/02-strings.xml
@@ -345,6 +345,6 @@
+
-
- Email address is required
- Password is required
+ Retpoŝta adreso estas postulata
+ Pasvorto estas postulata
diff --git a/AnkiDroid/src/main/res/values-eo/03-dialogs.xml b/AnkiDroid/src/main/res/values-eo/03-dialogs.xml
index e0fdb9056367..828cb82a9deb 100644
--- a/AnkiDroid/src/main/res/values-eo/03-dialogs.xml
+++ b/AnkiDroid/src/main/res/values-eo/03-dialogs.xml
@@ -228,8 +228,8 @@
Discord
Facebook
Twitter
- Send troubleshooting report
- Report already submitted
+ Sendi raporton pri problemo
+ Raporto sendita
- Aŭtomata samtempigo povas startigi post %d sekundo
diff --git a/AnkiDroid/src/main/res/values-eo/10-preferences.xml b/AnkiDroid/src/main/res/values-eo/10-preferences.xml
index 2527abaf092b..41220b832a35 100644
--- a/AnkiDroid/src/main/res/values-eo/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-eo/10-preferences.xml
@@ -173,7 +173,7 @@
Limigo de lernada tempero (timebox)
XXX min
Pritraktado de nova horzono
- Start of next day (0 is midnight, 23 is 11PM)
+ Komenco de tago (0 estas la horo 00:00. 23 estas la horo 23:00)
XXX horo(j) post noktmezo
Eksperimenta V2 planilo
Aktivigi testan planilon V2. Devigas ŝanĝojn en unu direkto je la sekva samtempigo
@@ -290,5 +290,5 @@
Simpla aranĝo de enigita respondo
Ripari “” aperantan en entajpita respondo
- Open Changelog
+ Montri ŝanĝoprotokolon
diff --git a/AnkiDroid/src/main/res/values-es-rAR/01-core.xml b/AnkiDroid/src/main/res/values-es-rAR/01-core.xml
index 8def9e7958c4..b6659d3e6071 100644
--- a/AnkiDroid/src/main/res/values-es-rAR/01-core.xml
+++ b/AnkiDroid/src/main/res/values-es-rAR/01-core.xml
@@ -71,7 +71,7 @@
- %d día %d restante
- %d días %d restantes
- Esta colección está vacía
+ La colección está vacía
Empieza añadiendo tarjetas usando el ícono +
diff --git a/AnkiDroid/src/main/res/values-es-rAR/02-strings.xml b/AnkiDroid/src/main/res/values-es-rAR/02-strings.xml
index c116360a9d0c..377d578ca01a 100644
--- a/AnkiDroid/src/main/res/values-es-rAR/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-es-rAR/02-strings.xml
@@ -345,6 +345,6 @@
+
-
- Email address is required
- Password is required
+ Se requiere dirección de correo electrónico
+ La contraseña es requerida
diff --git a/AnkiDroid/src/main/res/values-es-rES/01-core.xml b/AnkiDroid/src/main/res/values-es-rES/01-core.xml
index 4a50fb183cc8..323c1c8785e5 100644
--- a/AnkiDroid/src/main/res/values-es-rES/01-core.xml
+++ b/AnkiDroid/src/main/res/values-es-rES/01-core.xml
@@ -71,7 +71,7 @@
- %d día %d restante
- %d días %d restantes
- Esta colección está vacía
+ La colección está vacía
Empieza añadiendo tarjetas usando el ícono +
diff --git a/AnkiDroid/src/main/res/values-es-rES/02-strings.xml b/AnkiDroid/src/main/res/values-es-rES/02-strings.xml
index 855f589bd159..3a39e0344bc9 100644
--- a/AnkiDroid/src/main/res/values-es-rES/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-es-rES/02-strings.xml
@@ -345,6 +345,6 @@
+
-
- Email address is required
- Password is required
+ Se requiere dirección de correo electrónico
+ La contraseña es requerida
diff --git a/AnkiDroid/src/main/res/values-fa/01-core.xml b/AnkiDroid/src/main/res/values-fa/01-core.xml
index b5e40044f04b..d8f88670bb68 100644
--- a/AnkiDroid/src/main/res/values-fa/01-core.xml
+++ b/AnkiDroid/src/main/res/values-fa/01-core.xml
@@ -202,12 +202,12 @@
مرورگر کارت
کارت آنکی
- %1$s has a problem:
%2$s
- More information
- Found \'{{%1$s}}\', but there is no field called \'%1$s\'.
- Found \'{{/%1$s}}\', but missing \'{{#%1$s}}\' or \'{{^%1$s}}\'.
- Missing \'}}\' in \'%s\'.
- Missing \'{{%s}}\'.
- Found \'{{/%1$s}}\', but expected \'{{/%2$s}}\'
- Error in filter %s
+ %1$s دارای مشکل
%2$s است
+ اطلاعات بیشتر
+ \'{{%1$s}}\' پیدا شد، اما فیلدی به نام %1$s وجود ندارد
+ {{/%1$s}}\' پیدا شد، اما \'{{#%1$s}}\' یا \'{{^%1$s}}\' وجود ندارد.
+ \'}}\' در \'%s\' وجود ندارد.
+ \'{{%s}}\' وجود ندارد.
+ به جای \'{{/%2$s}}\'، \'{{/%1$s}}\' به کار رفته
+ خطا در فیلتر %s
diff --git a/AnkiDroid/src/main/res/values-fa/02-strings.xml b/AnkiDroid/src/main/res/values-fa/02-strings.xml
index 3473adc24c61..0035c7364d1b 100644
--- a/AnkiDroid/src/main/res/values-fa/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-fa/02-strings.xml
@@ -97,8 +97,8 @@
- زمان ویرایش یادداشت
- Modify today’s new card limit
- Modify today’s review card limit
+ تعیین محدودیت برای کارت جدید امروز
+ تعیین محدودیت برای مرور کارت امروز
مرور کارت های فراموش شده
مرور آنهایي که در شرف مطالعه اند
مطالعه کارت ها به صورت تصادفی
@@ -110,7 +110,7 @@
کارتی ساخته نشد. لطفاً فیلدهای بیشتری را پر کنید
فیلد نخست خالی است
این نوع یادداشت باید حاوی کلوز باشد
- Set field language
+ تنظیم زبان زمینه
نوع یادداشت فعلی هیچ کارتی تولید نکرد.\nلطفاٌ نوع یادداشت دیگری را انتخاب کنید، یا روی \'کارتها\' کلیک کرده یک فیلد جایگزین را وارد کنید
Cloze deletions will only work on a Cloze note type
ذخیره یادداشت
@@ -198,7 +198,7 @@
فایل %1$s قابل وارد کردن یافت نشد
گزینه ها
Deck options
- Study options
+ تنظیمات مطالعه
تنظیم زبان تبدیل متن به صدا TTS
مطالعه بر اساس تنظیمات دلخواه
بیشتر
@@ -239,7 +239,7 @@
- %s seconds
+ %s ثانیه
@@ -261,7 +261,7 @@
دستگاه شما نرم افزار قادر به انجام این عمل را ندارد.
مرورگر یافت نشد، لطفاً صفحۀ مربوطه را در سیستم دیگری باز کنید:
- Error loading page: %s
+ خطای بارگذاری صفحه: %s
لینک صفحۀ خارجی درون کارت پشتیبانی نمیشود.
کارتهای بازیابی شده
@@ -279,34 +279,34 @@
تصویر تخته سفید در %s ذخیره شد
رنگ ماژیک تخته سفید
- Empty Cards.]]>
- Empty first field: %s
- First field matched: %s
+ کارت های خالی را اجرا کنید.]]>
+ فیلد اول خالی: %s
+ فیلد اول مطابق با: %s
Added duplicate with first field: %s
- Appeared twice in file: %s
+ دوبار در فایل %s وجود دارد
One or more notes were not imported, because they didn\'t generate any cards. This can happen when you have empty fields or when you have not mapped the content in the text file to the correct fields
‘%1$s’ had %2$d fields, expected %3$d
Aborted: %s
Detected automated test. If you are a human, contact AnkiDroid support
- - %d note added
- - %d notes added
+ - %d یادداشت اضافه شد
+ - %d یادداشت اضافه شد
- - %d note updated
- - %d notes updated
+ - %d یادداشت به روزرسانی شد
+ - %d یادداشت به روزرسانی شد
- - %d note unchanged
- - %d notes unchanged
+ - %d یادداشت بدون تغییر
+ - %d یادداشت بدون تغییر
This card uses unsupported AnkiDroid features. Contact developer %1$s, or view the wiki. %2$s
Card provided invalid data. %s
Invalid AnkiDroid JS API version. Contact developer %s, or view wiki
AnkiDroid JS API update available. Contact developer %s, or view wiki
- View
- (Error Code: %d)
+ نما
+ (شماره خطا: %d)
Copied debug information to clipboard
Error copying debug information to clipboard
@@ -316,12 +316,12 @@
Loading http resources is no longer supported
Failed to load deck ‘%s’
- Search decks
- Fatal Error
+ جستجو در تمام دستهها
+ خطای مخرب
AnkiDroid relies on the System WebView which is unavailable. This can happen if the system is installing updates. Please try again in a few minutes.\n\n%s
- Font Size
- Show Toolbar
+ اندازه فونت
+ نمایش نوار ابزار
Format as Bold
Format as Italic
Format as Underline
diff --git a/AnkiDroid/src/main/res/values-fa/03-dialogs.xml b/AnkiDroid/src/main/res/values-fa/03-dialogs.xml
index d87b2c5dbcd7..dea38462749c 100644
--- a/AnkiDroid/src/main/res/values-fa/03-dialogs.xml
+++ b/AnkiDroid/src/main/res/values-fa/03-dialogs.xml
@@ -146,11 +146,11 @@
هیچ فایل مفقود شده یا بلا استفاده یافت نشد
پایگاه داده رسانه بازسازی شد
حذف استفاده نشده ها
- Deleting media…
- Deletion result
+ حذف رسانه…
+ نتیجه حذف
- - %d file deleted
- - %d files deleted
+ - %d فایل حذف شد
+ - %d فایل حذف شد
همه کارتها
@@ -158,7 +158,7 @@
موعد
در حال پیدا کردن کارت های خالی…
- Do you want to cancel?
+ کنسل میکنید؟
کارت خالی یافت نشد
کارتها برای اینکه حذف شوند: %d
کارتهای حذف شده: %d
@@ -189,7 +189,7 @@
دیتابیس قفل است
نرمافزاری دیگر در حال استفاده از دیتابیس آنکیدروید است. لطفاً نرم افزار دیگر را ببندید سپس آنکیدروید را باز کنید.
- Incompatible Database Version
+ نسخه پایگاه داده ناسازگار است
The database is a more advanced version than this version of AnkiDroid can work with. Upgrade AnkiDroid or downgrade the database to open it\n\nSupported version: %1$d\nDatabase version: %2$d\n\nThe following restore options will overwrite your current collection, possibly with a compatible database version:
تصویر پسزمینه اعمال شد
@@ -209,32 +209,32 @@
تنظیم یادآور انجام نشد
یادآورهای زیادی تنظیم شدهاند. بعضی از آنها نمایش داده نخواهند شد
- Keyboard Language Selection
+ انتخاب زبان کیبرد
Some multilingual keyboards, such as GBoard support changing language when editing text\n\nPlease request the “setImeHintLocales” feature from your keyboard manufacturer if your keyboard does not change language.
- Using AnkiDroid
- AnkiDroid Manual
- Anki Manual
+ استفاده از AnkiDroid
+ دفترچه راهنما AnkiDroid
+ دفتر راهنما Anki
AnkiDroid FAQ
- Get Help
- Mailing List
- Reddit
- Report a Bug
- Support AnkiDroid
- Donate
- Develop
- Rate
- Other
- Community
- Anki Forums
- Discord
- Facebook
- Twitter
- Send troubleshooting report
- Report already submitted
+ کمک گرفتن
+ لیست ایمیلها
+ ردیت
+ گزارش مشکل
+ درباره AnkiDroid
+ کمک مالی
+ توسعه
+ امتیازدهی
+ دیگر
+ انجمن
+ فوروم های Anki
+ دیسکورد
+ فیسبوک
+ توییتر
+ ارسال گزارش عیب یابی
+ گزارش قبلا ثبت شده است
- - An automatic sync may be triggered in %d second
- - An automatic sync may be triggered in %d seconds
+ - همگام سازی خودکار %d ثانیه دیگر آغاز میشود
+ - همگام سازی خودکار %d ثانیه دیگر آغاز میشود
diff --git a/AnkiDroid/src/main/res/values-fa/04-network.xml b/AnkiDroid/src/main/res/values-fa/04-network.xml
index 32110d6e320f..00d3c53bd99b 100644
--- a/AnkiDroid/src/main/res/values-fa/04-network.xml
+++ b/AnkiDroid/src/main/res/values-fa/04-network.xml
@@ -128,5 +128,5 @@
همسان کردن (کامل)
همسان کردن (ورود به حساب)
- Could not connect to AnkiWeb. Is your internet working?\n\n%s
+ عدم اتصال به AnkiWeb، آیا اتصال به اینترنت برقرار است؟ \n\n%s
diff --git a/AnkiDroid/src/main/res/values-fa/16-multimedia-editor.xml b/AnkiDroid/src/main/res/values-fa/16-multimedia-editor.xml
index ba4f03190b65..fcf64c71ac41 100644
--- a/AnkiDroid/src/main/res/values-fa/16-multimedia-editor.xml
+++ b/AnkiDroid/src/main/res/values-fa/16-multimedia-editor.xml
@@ -39,7 +39,7 @@
ضبط صدا
ویرایشگر پیشرفته
انصراف از پاک کردن
- Clear field
+ پاک کردن فیلد
قدرت گرفته از glosbe.com
diff --git a/AnkiDroid/src/main/res/values-fr/01-core.xml b/AnkiDroid/src/main/res/values-fr/01-core.xml
index d3f04464cb3b..c92416f5f6f7 100644
--- a/AnkiDroid/src/main/res/values-fr/01-core.xml
+++ b/AnkiDroid/src/main/res/values-fr/01-core.xml
@@ -184,7 +184,7 @@
Forcer le paquet
Il faut au moins un type de carte.
- - Supprimer le type de carte “%2$s” et son %1$d carte ?
+ - Supprimer le type de carte « %2$s » et son %1$d carte ?
- Supprimer le type de carte “%2$s” et ses %1$d cartes ?
Une carte n\'a pas pu être générée
diff --git a/AnkiDroid/src/main/res/values-fr/02-strings.xml b/AnkiDroid/src/main/res/values-fr/02-strings.xml
index 0f4a953727bc..9a16cbe10e8c 100644
--- a/AnkiDroid/src/main/res/values-fr/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-fr/02-strings.xml
@@ -187,7 +187,7 @@
Fichier Anki sauvegardé avec succès
Échec de la sauvegarde du fichier Anki
Le fichier « %s » a été exporté. Voulez-vous le partager à l\'aide d\'une autre app ?
- AnkiDroid a exporté %s flashcards
+ AnkiDroid a exporté %s cartes mémoire
Ceci est un paquet Anki envoyé depuis AnkiDroid.
@@ -345,6 +345,6 @@
+
-
- L\'adresse e-mail est requise
+ L\'adresse courriel est requise
Mot de passe requis
diff --git a/AnkiDroid/src/main/res/values-fr/10-preferences.xml b/AnkiDroid/src/main/res/values-fr/10-preferences.xml
index fe4f534595c9..8277b76efec8 100644
--- a/AnkiDroid/src/main/res/values-fr/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-fr/10-preferences.xml
@@ -173,7 +173,7 @@
Limite de temps
XXX min
Nouvelle gestion du fuseau horaire
- Start of next day (0 is midnight, 23 is 11PM)
+ Début du jour suivant (0 est minuit, 23 est 23 h)
XXX heures après minuit
Planificateur V2 expérimental
Activer le planificateur expérimental. Force les changements dans un sens lors de la prochaine synchronisation
@@ -290,5 +290,5 @@
Formatage simple des réponses saisies
Fixe « <unk> » dans les résultats des types de réponses
- Open Changelog
+ Ouvrir le journal des modifications
diff --git a/AnkiDroid/src/main/res/values-hi/03-dialogs.xml b/AnkiDroid/src/main/res/values-hi/03-dialogs.xml
index 4a461f72ad71..649bd53b9e25 100644
--- a/AnkiDroid/src/main/res/values-hi/03-dialogs.xml
+++ b/AnkiDroid/src/main/res/values-hi/03-dialogs.xml
@@ -219,7 +219,7 @@
Reddit
Report a Bug
AnkiDroid का समर्थन करें
- Donate
+ दान करें
Develop
Rate
अन्य
diff --git a/AnkiDroid/src/main/res/values-hi/07-cardbrowser.xml b/AnkiDroid/src/main/res/values-hi/07-cardbrowser.xml
index 4c56d617b3c7..87b5883a666e 100644
--- a/AnkiDroid/src/main/res/values-hi/07-cardbrowser.xml
+++ b/AnkiDroid/src/main/res/values-hi/07-cardbrowser.xml
@@ -87,15 +87,15 @@
(अध्ययन)
No cards found in deck ‘%s’
Search all decks
- Unknown
+ अज्ञात
- - %d card deleted
- - %d cards deleted
+ - %d कार्ड हटाया गया
+ - %d कार्ड हटाए गए
- Added
- First Review
- Latest Review
+ जोड़ा गया
+ पहली समीक्षा
+ नवीनतम समीक्षा
Interval
आसानी
Reviews
diff --git a/AnkiDroid/src/main/res/values-hi/10-preferences.xml b/AnkiDroid/src/main/res/values-hi/10-preferences.xml
index 20dc83bd2de7..93387e9bbce8 100644
--- a/AnkiDroid/src/main/res/values-hi/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-hi/10-preferences.xml
@@ -173,7 +173,7 @@
Timebox समय सीमा
XXX min
New timezone handling
- Start of next day (0 is midnight, 23 is 11PM)
+ अगले दिन की शुरुआत (0 आधी रात है, 23 11PM है)
XXX घंटे पिछले आधी रात
प्रायोगिक V2 अनुसूचक
Enable the experimental scheduler. Forces changes in one direction on next sync
diff --git a/AnkiDroid/src/main/res/values-it/01-core.xml b/AnkiDroid/src/main/res/values-it/01-core.xml
index ca124b4f23eb..d0d9eb246d63 100644
--- a/AnkiDroid/src/main/res/values-it/01-core.xml
+++ b/AnkiDroid/src/main/res/values-it/01-core.xml
@@ -184,8 +184,8 @@
Sovrascrittura Mazzo
È necessario almeno un tipo di carta.
- - Eliminare il tipo di carta \'%2$s\', e la sua carta %1$d?
- - Eliminare il tipo di carta \'%2$s\', e le sue carte %1$d?
+ - Eliminare il tipo di carta «%2$s», e la sua carta %1$d?
+ - Eliminare il tipo di carta «%2$s», e le sue carte %1$d?
Una carta non può essere generata
La rimozione di questo tipo di carta causerebbe l\'eliminazione di una o più note. Si prega di creare e salvare prima un nuovo tipo di carta.
diff --git a/AnkiDroid/src/main/res/values-it/02-strings.xml b/AnkiDroid/src/main/res/values-it/02-strings.xml
index 408e2cc4c3cb..af544e900c12 100644
--- a/AnkiDroid/src/main/res/values-it/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-it/02-strings.xml
@@ -313,7 +313,7 @@
Errore nel contenuto della carta: impossibile caricare «%s»
Errore del Contenuto della Carta: Richiesto aggiornamento multimediale
- Loading http resources is no longer supported
+ Il caricamento delle risorse http non è più supportato
Impossibile caricare il mazzo «%s»
Cerca mazzi
@@ -345,6 +345,6 @@
+
-
- Email address is required
- Password is required
+ L\'indirizzo di posta elettronica è obbligatorio.
+ La password è obbligatoria
diff --git a/AnkiDroid/src/main/res/values-it/03-dialogs.xml b/AnkiDroid/src/main/res/values-it/03-dialogs.xml
index 78932e1cd271..7ad0d1797d8d 100644
--- a/AnkiDroid/src/main/res/values-it/03-dialogs.xml
+++ b/AnkiDroid/src/main/res/values-it/03-dialogs.xml
@@ -215,7 +215,7 @@
Manuale di Anki
FAQ di AnkiDroid
Chiedi Aiuto
- Mailing List
+ Lista di diffusione
Reddit
Segnala un Bug
Supporta AnkiDroid
@@ -228,8 +228,8 @@
Discord
Facebook
Twitter
- Send troubleshooting report
- Report already submitted
+ Invia un rapporto sulla risoluzione dei problemi
+ Rapporto già inviato
- Una sincronizzazione automatica potrebbe essere attivata in %d secondo
diff --git a/AnkiDroid/src/main/res/values-it/04-network.xml b/AnkiDroid/src/main/res/values-it/04-network.xml
index 5a0b5c4e7ce0..c66919fa4030 100644
--- a/AnkiDroid/src/main/res/values-it/04-network.xml
+++ b/AnkiDroid/src/main/res/values-it/04-network.xml
@@ -58,7 +58,7 @@
Connesso come
Esci
Accesso in corso, attendi...
- Indirizzo email o password errati
+ Indirizzo di posta elettronica o password errati
Reimposta la password
Preferenze
diff --git a/AnkiDroid/src/main/res/values-it/10-preferences.xml b/AnkiDroid/src/main/res/values-it/10-preferences.xml
index f8aca02b43d3..7bb07cc39aab 100644
--- a/AnkiDroid/src/main/res/values-it/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-it/10-preferences.xml
@@ -173,7 +173,7 @@
Limite di tempo per sessione
XXX min
Nuova gestione fuso orario
- Start of next day (0 is midnight, 23 is 11PM)
+ Inizio del giorno successivo (0 è mezzanotte, 23 è 23 ore)
XXX ore dopo mezzanotte
Pianificatore sperimentale V2
Abilita la pianificazione sperimentale. Forza le modifiche in una direzione alla prossima sincronizzazione
@@ -290,5 +290,5 @@
Formattazione semplice della risposta
Correzioni ‘’ che appaiono nei risultati delle risposte digitate
- Open Changelog
+ Apri il registro dei cambiamenti
diff --git a/AnkiDroid/src/main/res/values-it/18-standard-models.xml b/AnkiDroid/src/main/res/values-it/18-standard-models.xml
index cfba3d499168..b05f2907e2fc 100644
--- a/AnkiDroid/src/main/res/values-it/18-standard-models.xml
+++ b/AnkiDroid/src/main/res/values-it/18-standard-models.xml
@@ -20,8 +20,8 @@
- Anteriore
- Indietro
+ Fronte
+ Retro
Testo
Extra
Aggiungi inverso
diff --git a/AnkiDroid/src/main/res/values-or/02-strings.xml b/AnkiDroid/src/main/res/values-or/02-strings.xml
index ccf90972ce50..d2ccf93bef98 100644
--- a/AnkiDroid/src/main/res/values-or/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-or/02-strings.xml
@@ -145,7 +145,7 @@
- %dଟି କାର୍ଡ ଯୋଗ କରାଯାଇଛି
ଡାଟାବେସ୍ ଯାଞ୍ଚ
- ମିଡ଼ିଆ ଯାଞ୍ଚ କରନ୍ତୁ
+ ମିଡ଼ିଆ ଯାଞ୍ଚ
ଖାଲି କାର୍ଡଗୁଡ଼ିକ
ଡାଟାବେସ୍ ଯାଞ୍ଚ କରାଯାଉଛି...
ଏହି ପତ୍ରଟି ଖାଲି ଅଛି। ତାସଖଣ୍ଡ ଚିଠାରୁ ମେନୁରେ \"ଖାଲି କାର୍ଡଗୁଡ଼ିକ\" ବିକଳ୍ପ ବାଛନ୍ତୁ।
diff --git a/AnkiDroid/src/main/res/values-or/03-dialogs.xml b/AnkiDroid/src/main/res/values-or/03-dialogs.xml
index afd5b43de116..ed1e0ba87e53 100644
--- a/AnkiDroid/src/main/res/values-or/03-dialogs.xml
+++ b/AnkiDroid/src/main/res/values-or/03-dialogs.xml
@@ -54,8 +54,7 @@
TTS ଆରମ୍ଭ ହେଉଛି...
କଥା କୁହ ନାହିଁ
- - %1$s:
ଆଣ୍ଡ୍ରଏଡ୍ ପାଇଁ ଫ୍ଲାସକାର୍ଡAnkiDroid ହେଉଛି ଏକ ଫ୍ଲାସକାର୍ଡ ଆପ୍ଲିକେସନ୍ ଯାହା ଡେସ୍କଟପ୍ ସଫ୍ଟୱେର୍
-Anki ସହିତ ସମ୍ପୂର୍ଣ୍ଣ ସୁସଙ୍ଗତ। ଆପଣ ଉଭୟ Ankidroid କିମ୍ବା Anki ଡେସ୍କଟପରେ ସାମଗ୍ରୀ ସୃଷ୍ଟି, ଡାଉନଲୋଡ୍ ଏବଂ ଶିଖିପାରିବେ, ଏବଂ ସେମାନଙ୍କୁ AnkiWeb ସହିତ ସହଜରେ ସିଙ୍କ୍ରୋନାଇଜ୍ କରିପାରିବେ।]]>
+ - %1$s:
ଆଣ୍ଡ୍ରଏଡ୍ ପାଇଁ ଫ୍ଲାସକାର୍ଡAnkiDroid ହେଉଛି ଏକ ଫ୍ଲାସକାର୍ଡ ଆପ୍ଲିକେସନ୍ ଯାହା ଡେସ୍କଟପ୍ ସଫ୍ଟୱେର୍ Anki ସହିତ ସମ୍ପୂର୍ଣ୍ଣ ସୁସଙ୍ଗତ। ଆପଣ ଉଭୟ Ankidroid କିମ୍ବା Anki ଡେସ୍କଟପରେ ସାମଗ୍ରୀ ସୃଷ୍ଟି, ଡାଉନଲୋଡ୍ ଏବଂ ଶିଖିପାରିବେ, ଏବଂ ସେମାନଙ୍କୁ AnkiWeb ସହିତ ସହଜରେ ସିଙ୍କ୍ରୋନାଇଜ୍ କରିପାରିବେ।]]>
- issue trackerକୁ ତ୍ରୁଟି ରିପୋର୍ଟ କରନ୍ତୁ; ନୂତନ ବୈଶିଷ୍ଟ୍ୟ ଚିନ୍ତାଧାରା ମଧ୍ୟ ବହୁତ ସ୍ୱାଗତଯୋଗ୍ୟ। Wikiରେ ଉତ୍ତର ଖୋଜ କିମ୍ବା Forumରେ ଏକ ଆଲୋଚନା ଆରମ୍ଭ କର।]]>
- ଖୋଲା-ଉତ୍ସ ସଫ୍ଟୱେର, ତେଣୁ ସମସ୍ତଙ୍କୁ ଯୋଗଦାନ କରିବାକୁ ଉତ୍ସାହିତ କରାଯାଏ :-)]]>
- ଅନୁବାଦ କରିବା, Wiki ଅପଡେଟ୍ କରିବା, screenshot ପ୍ରଦାନ କରିବା, କିମ୍ବା Ankidroid ବିଷୟରେ blog କରିବାରେ ସାହାଯ୍ୟ କରିପାରିବେ।]]>
@@ -64,10 +63,10 @@
ପୂର୍ବାବଲୋକନ
ନୂତନ କାର୍ଡଗୁଡ଼ିକ ପୁନଃସ୍ଥାନିତ କରନ୍ତୁ
ଆରମ୍ଭ ସ୍ଥିତି:
- କେବଳ ନୂତନ କାର୍ଡ ପୁନଃଅବସ୍ଥିତ କରାଯାଇପାରିବ
+ କେବଳ ନୂତନ କାର୍ଡ ବିଳମ୍ବ କରାଯାଇପାରିବ
- %d କାର୍ଡ ପୁନଃଅବସ୍ଥିତ
- - %dଟି କାର୍ଡ ପୁନଃଅବସ୍ଥିତ
+ - %dଟି କାର୍ଡ ବିଳମ୍ବ
Reset card progress
ଏହି କାର୍ଡକୁ ନୂତନ କାର୍ଡ ଧାଡି ଶେଷରେ ରଖାଯିବ
@@ -89,7 +88,7 @@
- %d କାର୍ଡ reschedule ହୋଇଛି
- %dଟି କାର୍ଡ reschedule ହୋଇଛି
- ଏକ ନୂତନ ପତ୍ର ପୁନଃନିର୍ମାଣ କରାଯାଉଛି | ପୁନଃନିର୍ଦ୍ଧାରଣ ନୂତନ କାର୍ଡଗୁଡ଼ିକୁ ସମୀକ୍ଷା ପତ୍ରରେ ପରିଣତ କରେ | ନୂତନ ଧାଡିରେ ଏକ ନୂତନ ପତ୍ରର ସ୍ଥିତି ପରିବର୍ତ୍ତନ କରିବାକୁ ‘ଅବସ୍ଥାନ ପତ୍ର’ ବିକଳ୍ପ ବ୍ୟବହାର କରନ୍ତୁ
+ ଏକ ନୂତନ ପତ୍ର ପୁନଃନିର୍ମାଣ କରାଯାଉଛି। ପୁନଃନିର୍ଦ୍ଧାରଣ ନୂତନ କାର୍ଡଗୁଡ଼ିକୁ ସମୀକ୍ଷା ପତ୍ରରେ ପରିଣତ କରେ। ନୂତନ ଧାଡିରେ ଏକ ନୂତନ ପତ୍ରର ସ୍ଥିତି ପରିବର୍ତ୍ତନ କରିବାକୁ ‘ପତ୍ର ବିଳମ୍ବ’ ବିକଳ୍ପ ବ୍ୟବହାର କରନ୍ତୁ
ଭବିଷ୍ୟତରେ ନିର୍ଦ୍ଧାରକ କିପରି ପତ୍ରଙ୍କୁ ବ୍ୟବହାର କରେ ତାହା ମଧ୍ୟ ପୁନଃସେଟ୍ କଲେ ପୁନଃସେଟ୍ ହେଇଯାଏ (ସହଜତା %d%% ରେ ସେଟ୍ ହେବ)
ଆପଣଙ୍କର ଫାଇଲ୍ ମ୍ୟାନେଜର୍ ରେ ତାସଖଣ୍ଡ ଚୟନ କରନ୍ତୁ କିମ୍ବା ଏହାକୁ ପରିଦର୍ଶନ କରନ୍ତୁ: ଏହାକୁ ଆମଦାନୀ କରିବାକୁ ଡାଉନଲୋଡ୍ ପୃଷ୍ଠା
ଡାଟାବେସ୍ ତ୍ରୁଟି
diff --git a/AnkiDroid/src/main/res/values-or/04-network.xml b/AnkiDroid/src/main/res/values-or/04-network.xml
index cb9fc0a78df9..b2d5a615a7e6 100644
--- a/AnkiDroid/src/main/res/values-or/04-network.xml
+++ b/AnkiDroid/src/main/res/values-or/04-network.xml
@@ -79,8 +79,8 @@
, ସମୟ କ୍ଷେତ୍ର ସମ୍ଭବତ ଭୁଲ୍ ଅଟେ
, ତାରିଖ ସମ୍ଭବତ ଭୁଲ ଅଟେ
ସିଙ୍କ୍ ରେ ଦ୍ୱନ୍ଦ୍ୱ
- କୌଣସି ପରିବର୍ତ୍ତନ ମିଳିଲା ନାହିଁ |
- ସଂଗ୍ରହ ସମକାଳୀକ ହୋଇଛି |
+ କୌଣସି ପରିବର୍ତ୍ତନ ମିଳିଲା ନାହିଁ।
+ ସଂଗ୍ରହ ସମକାଳୀକ ହୋଇଛି।
ସିଙ୍କ୍ ତ୍ରୁଟି, ପ୍ରକାର: %1$s, ବାର୍ତ୍ତା: %2$s
ଆପଣଙ୍କର ଡାଟାବେସ୍ ଭ୍ରଷ୍ଟ ଅଟେ। ସିଙ୍କ୍ କରିବାକୁ ପୁନର୍ବାର ଚେଷ୍ଟା କରିବା ପୂର୍ବରୁ ଦୟାକରି ଏହାକୁ ଠିକ୍ କରନ୍ତୁ।\n\nଆପଣଙ୍କର ଡାଟାବେସ୍ ମରାମତି ବିଷୟରେ ସୂଚନା ପାଇଁ %s ଦେଖନ୍ତୁ।
ସର୍ଭର ବ୍ୟସ୍ତ | ପରେ ପୁନର୍ବାର ଚେଷ୍ଟା କରନ୍ତୁ |
diff --git a/AnkiDroid/src/main/res/values-or/09-backup.xml b/AnkiDroid/src/main/res/values-or/09-backup.xml
index d0ee3f8a0e36..059305ed6cbc 100644
--- a/AnkiDroid/src/main/res/values-or/09-backup.xml
+++ b/AnkiDroid/src/main/res/values-or/09-backup.xml
@@ -40,7 +40,7 @@
ବ୍ୟାକଅପ୍ ସଞ୍ଚୟ ହୋଇନାହିଁ। SD କାର୍ଡରେ ପର୍ଯ୍ୟାପ୍ତ ସ୍ଥାନ ବାକି ନାହିଁ। ବ୍ୟାକଅପ୍ ଗଭୀରତାକୁ କମ୍ କରନ୍ତୁ କିମ୍ବା ଅନ୍ୟ କିଛି ଫାଇଲ୍ ଅପସାରଣ କରନ୍ତୁ।
ଆପଣଙ୍କ SD କାର୍ଡରେ %d MB ରୁ କମ୍ ସ୍ଥାନ ବାକି ଅଛି।\nଡାଟା ନଷ୍ଟକୁ ଏଡାଇବା ପାଇଁ କିଛି ସ୍ଥାନ ଖାଲି କରନ୍ତୁ।
- ବ୍ୟାକଅପ୍ ରୁ ପୁନରୁଦ୍ଧାର କରନ୍ତୁ
+ ବ୍ୟାକଅପ୍ ରୁ ପୁନରୁଦ୍ଧାର
ନୂତନ ସଂଗ୍ରହ
ସଂଗ୍ରହ ଅପସାରଣ କରନ୍ତୁ ଏବଂ ନୂତନ ନିର୍ମାଣ କରନ୍ତୁ
ସଂଗ୍ରହ ବିଲୋପ କରନ୍ତୁ ଏବଂ ଏକ ନୂତନ ସୃଷ୍ଟି କରନ୍ତୁ? ଏହା ଆପଣଙ୍କର ସମସ୍ତ ଶିକ୍ଷଣ ପ୍ରଗତିକୁ ଛାଡ଼ି ଦେବ ଏବଂ ସମସ୍ତ ପତ୍ର ବିଲୋପ କରିବ |
diff --git a/AnkiDroid/src/main/res/values-or/10-preferences.xml b/AnkiDroid/src/main/res/values-or/10-preferences.xml
index 4ca1859f4db9..d424fa55291a 100644
--- a/AnkiDroid/src/main/res/values-or/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-or/10-preferences.xml
@@ -139,7 +139,7 @@
ଭାଷା
ସିଷ୍ଟମ୍ ଭାଷା
ବିଜ୍ଞପ୍ତିଗୁଡିକ
- କେତେବେଳେ ସୂଚିତ କର
+ କେତେବେଳେ ସୂଚିତ କରିବି
କଦାପି ସୂଚିତ କରନାହିଁ
ବିଚାରାଧୀନ ବାର୍ତ୍ତା ଉପଲବ୍ଧ
%d ରୁ ଅଧିକ ପତ୍ର ବାକି ଅଛି
@@ -173,7 +173,7 @@
ଟାଇମ୍ ବକ୍ସ ସମୟ ସୀମା
XXX ମିନିଟ୍
ନୂତନ ସମୟ କ୍ଷେତ୍ର ପରିଚାଳନା
- Start of next day (0 is midnight, 23 is 11PM)
+ ପରବର୍ତ୍ତୀ ଦିନର ଆରମ୍ଭ (0 ଅର୍ଥ ମଧ୍ୟରାତ୍ରି, 23 ହେଉଛି 11 PM)
XXX ଘଣ୍ଟା ମଧ୍ୟରାତ୍ରି
ପରୀକ୍ଷାମୂଳକ V2 ନିର୍ଧାରକ |
ପରୀକ୍ଷାମୂଳକ କାର୍ଯ୍ୟସୂଚୀ ସକ୍ଷମ କରନ୍ତୁ | ପରବର୍ତ୍ତୀ ସିଙ୍କରେ ଗୋଟିଏ ଦିଗରେ ପରିବର୍ତ୍ତନ କରିବାକୁ ବାଧ୍ୟ କରେ
diff --git a/AnkiDroid/src/main/res/values-or/17-model-manager.xml b/AnkiDroid/src/main/res/values-or/17-model-manager.xml
index 630437921083..14e7cbc17025 100644
--- a/AnkiDroid/src/main/res/values-or/17-model-manager.xml
+++ b/AnkiDroid/src/main/res/values-or/17-model-manager.xml
@@ -20,7 +20,7 @@
- ନୋଟ୍ ପ୍ରକାରଗୁଡ଼ିକ ପରିଚାଳନା କରନ୍ତୁ
+ ନୋଟ୍ ପ୍ରକାରଗୁଡ଼ିକ ପରିଚାଳନା
କ୍ଷେତ୍ରଗୁଡିକ ପରିଚାଳନା କର
ଆପଣ ଆପଣଙ୍କର ଶେଷ ନୋଟ୍ ପ୍ରକାର ବିଲୋପ କରିପାରିବେ ନାହିଁ
diff --git a/AnkiDroid/src/main/res/values-pl/02-strings.xml b/AnkiDroid/src/main/res/values-pl/02-strings.xml
index d7e1d7055646..d4f09db74be2 100644
--- a/AnkiDroid/src/main/res/values-pl/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-pl/02-strings.xml
@@ -371,6 +371,6 @@
+
-
- Email address is required
- Password is required
+ Adres e-mail jest wymagany
+ Hasło jest wymagane
diff --git a/AnkiDroid/src/main/res/values-pl/03-dialogs.xml b/AnkiDroid/src/main/res/values-pl/03-dialogs.xml
index 952e93230a85..c9214875680d 100644
--- a/AnkiDroid/src/main/res/values-pl/03-dialogs.xml
+++ b/AnkiDroid/src/main/res/values-pl/03-dialogs.xml
@@ -243,7 +243,7 @@
Facebook
Twitter
Send troubleshooting report
- Report already submitted
+ Raport został już wysłany
- Automatyczna synchronizacja może zostać uruchomiona za %d sekundę
diff --git a/AnkiDroid/src/main/res/values-pl/10-preferences.xml b/AnkiDroid/src/main/res/values-pl/10-preferences.xml
index ac9c555b90c0..eb33b353e1f6 100644
--- a/AnkiDroid/src/main/res/values-pl/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-pl/10-preferences.xml
@@ -294,5 +294,5 @@
Proste formatowanie wpisywanej odpowiedzi
Naprawia ‘’ pojawiające się we wpisywanych odpowiedziach
- Open Changelog
+ Otwórz listę zmian w programie
diff --git a/AnkiDroid/src/main/res/values-sv/01-core.xml b/AnkiDroid/src/main/res/values-sv/01-core.xml
index 551bcd725b18..8507e6a90828 100644
--- a/AnkiDroid/src/main/res/values-sv/01-core.xml
+++ b/AnkiDroid/src/main/res/values-sv/01-core.xml
@@ -38,7 +38,7 @@
~ this program. If not, see .
-->
- yes
+ ja
Kortlekar
Kortbläddrare
@@ -64,15 +64,15 @@
- %d minuter kvar
- - %d hour %d left
- - %d hours %d left
+ - %d timme %d kvar
+ - %d timmar %d kvar
- - %d day %d left
- - %d days %d left
+ - %d dag %d kvar
+ - %d dagar %d kvar
- Collection is empty
- Start adding cards\nusing the + icon.
+ Samlingen är tom
+ Börja lägga till kort\nmed + knappen
- %1$d kort studerade i %2$s idag
@@ -81,7 +81,7 @@
Skriv svar
Visa svar
- Hide answer
+ Följ svar
Igen
Svår
Bra
@@ -94,7 +94,7 @@
Flytta alla till kortlek
Sluta dölja
Byt namn på lek
- Create shortcut
+ Skapa genväg
Lägg till
Lägg till not
Synkronisera konto
@@ -106,20 +106,20 @@
Dölj
Lås
Ta bort not
- Flag
- Flag card
- No flag
- Red flag
- Orange flag
- Green flag
- Blue flag
- Edit tags
+ Flagga
+ Flagga kort
+ Ingen flagga
+ Röd flagga
+ Orange flagga
+ Grön flagga
+ Blå flagga
+ Redigera taggar
Vill du verkligen ta bort den här noten och alla dess kort?\n%s
Markera text
Sökning i %1$s
Markera not
Avmarkera not
- Check pronunciation
+ Kontrollera uttal
Skapa kortlek
Skapa
Skapa filtrerad kortlek
@@ -129,7 +129,7 @@
The user is taken to the "App Info" screen, and needs to click "Permissions" and
grant AnkiDroid the \'Storage\' permission. This needs to be a fairly generic message as implementations
of the permissions/app info screen will differ between devices. -->
- Please grant AnkiDroid the ‘Storage’ permission to continue
+ Vänligen ge AnkiDroid behörigheten \'Lagring\' för att fortsätta
- Rebuilding filtered deck…
+ Bygger om filtrerad kortlek...
Bygg om
Töm
- Create subdeck
- Emptying filtered deck…
+ Skapa underkortlek
+ Tömmer filtrerad kortlek...
Anpassad studiesession
Var god döp om den existerande anpassade studiekortleken först.
Den här leken är tom
- Add card
- Invalid deck name
+ Lägg till kort
+ Ogiltigt kortleksnamn
Den här leken är tom. Använd +-knappen för att lägga till nytt innehåll.
Den dagliga studiegränsen har nåtts
Inga kort finns schemalagda
@@ -180,7 +180,7 @@
Kortmall för framsida
Kortmall för baksida
Formgivning
- Card Browser Appearance
+ Visning i kortbläddrare
Deck Override
Minst en korttyp krävs.
diff --git a/AnkiDroid/src/main/res/values-tt/02-strings.xml b/AnkiDroid/src/main/res/values-tt/02-strings.xml
index d5452df33dfe..4b86f7083edd 100644
--- a/AnkiDroid/src/main/res/values-tt/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-tt/02-strings.xml
@@ -216,7 +216,7 @@
- %s minutes
- - %s hours
+ - %s сәгатъ
- %s days
diff --git a/AnkiDroid/src/main/res/values-tt/03-dialogs.xml b/AnkiDroid/src/main/res/values-tt/03-dialogs.xml
index fd377f0c1728..be30fc66d92f 100644
--- a/AnkiDroid/src/main/res/values-tt/03-dialogs.xml
+++ b/AnkiDroid/src/main/res/values-tt/03-dialogs.xml
@@ -110,9 +110,9 @@
SD card almost full
Restore backup?
Your collection will be replaced with an older copy. Any unsaved progress will be lost.
- Cancel
+ Баш тарту
OK
- No
+ Юк
Continue
Create
Delete
diff --git a/AnkiDroid/src/main/res/values-tt/04-network.xml b/AnkiDroid/src/main/res/values-tt/04-network.xml
index d854e7ae54ae..cbc738948f8a 100644
--- a/AnkiDroid/src/main/res/values-tt/04-network.xml
+++ b/AnkiDroid/src/main/res/values-tt/04-network.xml
@@ -42,7 +42,7 @@
Сез челтәрдә түгел
Try Anyway
Sync error
- Error
+ Хата
Retry
Get shared decks
A network error has occurred
diff --git a/AnkiDroid/src/main/res/values-uk/02-strings.xml b/AnkiDroid/src/main/res/values-uk/02-strings.xml
index e333c567e95f..a847d178169a 100644
--- a/AnkiDroid/src/main/res/values-uk/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-uk/02-strings.xml
@@ -371,6 +371,6 @@
+
-
- Email address is required
- Password is required
+ Необхідно вказати адресу електронної пошти
+ Поле «Пароль» обов\'язкове до заповнення
diff --git a/AnkiDroid/src/main/res/values-uk/03-dialogs.xml b/AnkiDroid/src/main/res/values-uk/03-dialogs.xml
index 4ceb72ef69dd..270d6572d7c5 100644
--- a/AnkiDroid/src/main/res/values-uk/03-dialogs.xml
+++ b/AnkiDroid/src/main/res/values-uk/03-dialogs.xml
@@ -242,8 +242,8 @@
Discord
Facebook
Twitter
- Send troubleshooting report
- Report already submitted
+ Надіслати звіт про несправності
+ Звіт вже надіслано
- Автоматична синхронізація може запуститися через %d секунду.
diff --git a/AnkiDroid/src/main/res/values-uk/10-preferences.xml b/AnkiDroid/src/main/res/values-uk/10-preferences.xml
index 1b4611bc2a7f..4c14006a15d9 100644
--- a/AnkiDroid/src/main/res/values-uk/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-uk/10-preferences.xml
@@ -174,7 +174,7 @@
Період лічильника переглядів
XXX хв.
Обробка нового часового поясу
- Start of next day (0 is midnight, 23 is 11PM)
+ Початок наступного дня (0 – опівніч, 23 – 23:00)
XXX год.
Експериментальний планувальник V2
Увімкнути експериментальний планувальник. Примушує зміни в одному напрямку при наступній синхронізації
diff --git a/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml b/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml
index f85f37504052..cd3a7dfeb7f3 100644
--- a/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-zh-rCN/10-preferences.xml
@@ -173,7 +173,7 @@
时间盒时间限制
XXX 分钟
新时区处理
- Start of next day (0 is midnight, 23 is 11PM)
+ 次日开始时间 (0为午夜,23为 11PM)
午夜过后的XXX小时
实验性 V2 调度器
启用实验性调度器。在下次同步时强制单向变更
diff --git a/docs/marketing/localized_description/marketdescription-ar.txt b/docs/marketing/localized_description/marketdescription-ar.txt
index 4002f5118e57..fe7ccccbc01f 100644
--- a/docs/marketing/localized_description/marketdescription-ar.txt
+++ b/docs/marketing/localized_description/marketdescription-ar.txt
@@ -12,7 +12,7 @@
• دعم محتويات البطاقات التعليمية: نصوص، صور، أصوات، LaTeX
• التكرار المتباعد (خوارزمية supermemo 2)
• دعم ميزة تحويل النص إلى كلام
-• أكثر من 6000 رزمة جاهزة
+• أكثر من 6000 شَدّة أوراق جاهزة
• عنصر واجهة تقدم التعلم
• إحصائيات مفصلة
• المزامنة مع أنكي ويب
diff --git a/docs/marketing/localized_description/marketdescription-fr.txt b/docs/marketing/localized_description/marketdescription-fr.txt
index 3ab0e4f7aadb..ed3ab95bf449 100644
--- a/docs/marketing/localized_description/marketdescription-fr.txt
+++ b/docs/marketing/localized_description/marketdescription-fr.txt
@@ -16,9 +16,9 @@ Ajoutez des cartes par le biais de l’application Anki pour ordinateur ou direc
• widget progression
• statistiques détaillées
• synchronisation avec AnkiWeb
-• open source
+• code source ouvert
-★ Fonctionnalités supplémentaires:
+★ Fonctionnalités supplémentaires :
• écrire les réponses (en option)
• tableau blanc
• éditeur de cartes
diff --git a/docs/marketing/localized_description/marketdescription-it.txt b/docs/marketing/localized_description/marketdescription-it.txt
index 9ab301e30276..2670ae854953 100644
--- a/docs/marketing/localized_description/marketdescription-it.txt
+++ b/docs/marketing/localized_description/marketdescription-it.txt
@@ -16,7 +16,7 @@ Aggiungi materiale con l'applicazione per desktop Anki o direttamente con AnkiDr
• widget con progresso
• statistiche dettagliate
• sincronizzazione con AnkiWeb
-• open source
+• codice sorgente aperto
★ Caratteristiche aggiuntive:
• risposte scritte (opzionale)
From 3cca3b1f6e4c8edf05386a4652fb5e34566ddf26 Mon Sep 17 00:00:00 2001
From: Mike Hardy
Date: Thu, 1 Apr 2021 01:01:19 +0000
Subject: [PATCH 019/171] Bumped version to 2.15alpha37 @branch-specific
---
AnkiDroid/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/AnkiDroid/build.gradle b/AnkiDroid/build.gradle
index c90b8aa15b4a..42b9bf6079b8 100644
--- a/AnkiDroid/build.gradle
+++ b/AnkiDroid/build.gradle
@@ -50,8 +50,8 @@ android {
//
// This ensures the correct ordering between the various types of releases (dev < alpha < beta < release) which is
// needed for upgrades to be offered correctly.
- versionCode=21500136
- versionName="2.15alpha36"
+ versionCode=21500137
+ versionName="2.15alpha37"
minSdkVersion 21
//noinspection OldTargetApi - also performed in api/build.fradle
targetSdkVersion 29 // change .travis.yml platform download at same time
From b722525f6322befbf28c08de46d0ca4beb16bf06 Mon Sep 17 00:00:00 2001
From: Arthur Milchior
Date: Wed, 31 Mar 2021 01:21:32 +0200
Subject: [PATCH 020/171] NF: indicate that mUndoNameId is a string ressource
---
AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
index b8251910fcde..1fbcef3c9669 100644
--- a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
+++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
@@ -80,6 +80,7 @@
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteStatement;
@@ -172,6 +173,7 @@ public enum DismissType {
RESCHEDULE_CARDS(R.string.card_editor_reschedule_card),
RESET_CARDS(R.string.card_editor_reset_card);
+ @StringRes
private final int mUndoNameId;
DismissType(int undoNameId) {
From 6cc4d6e2896c311a5c7ec35b5f33231e3717835c Mon Sep 17 00:00:00 2001
From: Akshay Vilas Jadhav <52353967+Akshay0701@users.noreply.github.com>
Date: Fri, 2 Apr 2021 03:19:40 +0530
Subject: [PATCH 021/171] Add deck search in NoteEditor (#8381)
* search deck in NoteEditor
* extracted "Deck Search" to resources
* removed onNeutral
---
.../com/ichi2/anki/CardTemplateEditor.java | 2 +-
.../main/java/com/ichi2/anki/NoteEditor.java | 43 +++++++++++++------
.../anki/dialogs/DeckSelectionDialog.java | 23 ++++++----
AnkiDroid/src/main/res/values/01-core.xml | 1 +
4 files changed, 47 insertions(+), 22 deletions(-)
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.java b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.java
index 3815c0612204..11384211a857 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.java
@@ -559,7 +559,7 @@ private void displayDeckOverrideDialog(Collection col, TemporaryModel tempModel)
FunctionalInterfaces.Filter nonDynamic = (d) -> !Decks.isDynamic(d);
List decks = SelectableDeck.fromCollection(col, nonDynamic);
String title = getString(R.string.card_template_editor_deck_override);
- DeckSelectionDialog dialog = DeckSelectionDialog.newInstance(title, explanation, decks);
+ DeckSelectionDialog dialog = DeckSelectionDialog.newInstance(title, explanation, true, decks);
AnkiActivity.showDialogFragment(activity, dialog);
}
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.java b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.java
index d2e5774c151c..5b012ebcb4f2 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.java
@@ -51,6 +51,7 @@
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
@@ -66,6 +67,7 @@
import com.afollestad.materialdialogs.MaterialDialog;
import com.ichi2.anki.dialogs.ConfirmationDialog;
+import com.ichi2.anki.dialogs.DeckSelectionDialog;
import com.ichi2.anki.dialogs.DiscardChangesDialog;
import com.ichi2.anki.dialogs.IntegerDialog;
import com.ichi2.anki.dialogs.LocaleSelectionDialog;
@@ -92,6 +94,7 @@
import com.ichi2.compat.CompatHelper;
import com.ichi2.libanki.Card;
import com.ichi2.libanki.Collection;
+import com.ichi2.libanki.Decks;
import com.ichi2.libanki.Models;
import com.ichi2.libanki.Model;
import com.ichi2.libanki.Note;
@@ -105,6 +108,7 @@
import com.ichi2.utils.ContentResolverUtil;
import com.ichi2.utils.DeckComparator;
import com.ichi2.utils.FileUtil;
+import com.ichi2.utils.FunctionalInterfaces;
import com.ichi2.utils.FunctionalInterfaces.Consumer;
import com.ichi2.utils.KeyUtils;
import com.ichi2.utils.MapUtil;
@@ -149,6 +153,7 @@
* @see the Anki Desktop manual
*/
public class NoteEditor extends AnkiActivity implements
+ DeckSelectionDialog.DeckSelectionListener,
TagsDialog.TagsDialogListener {
// DA 2020-04-13 - Refactoring Plans once tested:
// * There is a difference in functionality depending on whether we are editing
@@ -253,6 +258,22 @@ private SaveNoteHandler saveNoteHandler() {
return new SaveNoteHandler(this);
}
+
+ @Override
+ public void onDeckSelected(@Nullable DeckSelectionDialog.SelectableDeck deck) {
+ if (deck != null) {
+ mCurrentDid = deck.getDeckId();
+ mNoteDeckSpinner.setSelection(mAllDeckIds.indexOf(deck.getDeckId()), false);
+ }
+ }
+
+ private void displayDeckOverrideDialog(Collection col) {
+ FunctionalInterfaces.Filter nonDynamic = (d) -> !Decks.isDynamic(d);
+ List decks = DeckSelectionDialog.SelectableDeck.fromCollection(col, nonDynamic);
+ DeckSelectionDialog dialog = DeckSelectionDialog.newInstance(getString(R.string.search_deck), null, false, decks);
+ AnkiActivity.showDialogFragment(NoteEditor.this, dialog);
+ }
+
private enum AddClozeType {
SAME_NUMBER,
INCREMENT_NUMBER
@@ -623,19 +644,15 @@ public View getDropDownView(int position, View convertView, ViewGroup parent) {
};
mNoteDeckSpinner.setAdapter(noteDeckAdapter);
noteDeckAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- mNoteDeckSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
+ mNoteDeckSpinner.setOnTouchListener(new View.OnTouchListener() {
- @Override
- public void onItemSelected(AdapterView> parent, View view, int pos, long id) {
- // Timber.i("NoteEditor:: onItemSelected() fired on mNoteDeckSpinner with pos = %d", pos);
- mCurrentDid = mAllDeckIds.get(pos);
- }
-
- @Override
- public void onNothingSelected(AdapterView> parent) {
- // Do Nothing
- }
- });
+ public boolean onTouch(View view, MotionEvent motionEvent) {
+ if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
+ displayDeckOverrideDialog(getCol());
+ }
+ return true;
+ }
+ });
mCurrentDid = intent.getLongExtra(EXTRA_DID, mCurrentDid);
String mGetTextFromSearchView = intent.getStringExtra(EXTRA_TEXT_FROM_SEARCH_VIEW);
@@ -767,7 +784,7 @@ public boolean onKeyUp(int keyCode, KeyEvent event) {
case KeyEvent.KEYCODE_D:
//null check in case Spinner is moved into options menu in the future
if (event.isCtrlPressed() && (mNoteDeckSpinner != null)) {
- mNoteDeckSpinner.performClick();
+ displayDeckOverrideDialog(getCol());
}
break;
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckSelectionDialog.java b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckSelectionDialog.java
index 63a9f4506bee..78d02058eb84 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckSelectionDialog.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckSelectionDialog.java
@@ -59,11 +59,12 @@ public class DeckSelectionDialog extends AnalyticsDialogFragment {
* A dialog which handles selecting a deck
*/
@NonNull
- public static DeckSelectionDialog newInstance(@NonNull String title, @NonNull String summaryMessage, @NonNull List decks) {
+ public static DeckSelectionDialog newInstance(@NonNull String title, @Nullable String summaryMessage, @NonNull boolean keepRestoreDefaultButton, @NonNull List decks) {
DeckSelectionDialog f = new DeckSelectionDialog();
Bundle args = new Bundle();
args.putString("summaryMessage", summaryMessage);
args.putString("title", title);
+ args.putBoolean("keepRestoreDefaultButton", keepRestoreDefaultButton);
args.putParcelableArrayList("deckNames", new ArrayList<>(decks));
f.setArguments(args);
return f;
@@ -88,7 +89,12 @@ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Bundle arguments = requireArguments();
- summary.setText(getSummaryMessage(arguments));
+ if (getSummaryMessage(arguments) == null) {
+ summary.setVisibility(View.GONE);
+ } else {
+ summary.setVisibility(View.VISIBLE);
+ summary.setText(getSummaryMessage(arguments));
+ }
RecyclerView recyclerView = dialogView.findViewById(R.id.deck_picker_dialog_list);
recyclerView.requestFocus();
@@ -108,19 +114,20 @@ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
MaterialDialog.Builder builder = new MaterialDialog.Builder(requireActivity())
.neutralText(R.string.dialog_cancel)
- .negativeText(R.string.restore_default)
- .customView(dialogView, false)
- .onNegative((dialog, which) -> onDeckSelected(null))
- .onNeutral((dialog, which) -> { });
+ .customView(dialogView, false);
+
+ if (arguments.getBoolean("keepRestoreDefaultButton")) {
+ builder = builder.negativeText(R.string.restore_default).onNegative((dialog, which) -> onDeckSelected(null));
+ }
mDialog = builder.build();
return mDialog;
}
- @NonNull
+ @Nullable
private String getSummaryMessage(Bundle arguments) {
- return Objects.requireNonNull(arguments.getString("summaryMessage"));
+ return arguments.getString("summaryMessage");
}
diff --git a/AnkiDroid/src/main/res/values/01-core.xml b/AnkiDroid/src/main/res/values/01-core.xml
index 60a1c92b3dd5..b6e8b54c210c 100644
--- a/AnkiDroid/src/main/res/values/01-core.xml
+++ b/AnkiDroid/src/main/res/values/01-core.xml
@@ -138,6 +138,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
From 0376a0a39f063b329407dec1135b8522be4f9b3f Mon Sep 17 00:00:00 2001
From: David Allison <62114487+david-allison-1@users.noreply.github.com>
Date: Thu, 1 Apr 2021 06:52:24 +0100
Subject: [PATCH 022/171] Make launcher icon red in debug mode
I was struggling to tell the icons apart in my quick launch as the
app name was truncated
---
AnkiDroid/build.gradle | 5 +++++
.../src/main/res/drawable-v24/ic_launcher_foreground.xml | 4 ++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/AnkiDroid/build.gradle b/AnkiDroid/build.gradle
index 42b9bf6079b8..4c74aab63904 100644
--- a/AnkiDroid/build.gradle
+++ b/AnkiDroid/build.gradle
@@ -84,6 +84,9 @@ android {
testCoverageEnabled true
}
+ // make the icon red if in debug mode
+ resValue 'color', 'anki_foreground_icon_color_0', "#FFFF0000"
+ resValue 'color', 'anki_foreground_icon_color_1', "#FFFF0000"
}
release {
minifyEnabled true
@@ -91,6 +94,8 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
buildConfigField "String", "ACRA_URL", '"https://ankidroid.org/acra/report"'
+ resValue 'color', 'anki_foreground_icon_color_0', "#FF29B6F6"
+ resValue 'color', 'anki_foreground_icon_color_1', "#FF0288D1"
}
}
diff --git a/AnkiDroid/src/main/res/drawable-v24/ic_launcher_foreground.xml b/AnkiDroid/src/main/res/drawable-v24/ic_launcher_foreground.xml
index 2f527609283b..a1c98182a898 100644
--- a/AnkiDroid/src/main/res/drawable-v24/ic_launcher_foreground.xml
+++ b/AnkiDroid/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -18,8 +18,8 @@
android:endY="140.15863"
android:endX="164.13487"
android:type="linear">
-
-
+
+
From 750ba9b187f4c7a8c3b46af8d03bf820def9c29e Mon Sep 17 00:00:00 2001
From: Aditya-Srivastav <54016427+4D17Y4@users.noreply.github.com>
Date: Fri, 2 Apr 2021 03:57:58 +0530
Subject: [PATCH 023/171] Don't show "increase new card limit" in CustomStudy
dialog when no new cards' (#8338)
* Removed increase new card limit when no new cards
* Added Regression Test
* used string from resources and added en qualifier in regression test
* added annotations
Co-authored-by: Mike Hardy
---
.../ichi2/anki/dialogs/CustomStudyDialog.java | 15 ++++++++++--
.../anki/dialogs/CustomStudyDialogTest.java | 23 +++++++++++++++++++
2 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CustomStudyDialog.java b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CustomStudyDialog.java
index 22c28f9401d8..f9473553122e 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CustomStudyDialog.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/CustomStudyDialog.java
@@ -350,8 +350,19 @@ private int[] getListIds(int dialogId) {
switch (dialogId) {
case CONTEXT_MENU_STANDARD:
// Standard context menu
- return new int[] {CUSTOM_STUDY_NEW, CUSTOM_STUDY_REV, CUSTOM_STUDY_FORGOT, CUSTOM_STUDY_AHEAD,
- CUSTOM_STUDY_RANDOM, CUSTOM_STUDY_PREVIEW, CUSTOM_STUDY_TAGS};
+ ArrayList dialogOptions = new ArrayList();
+ dialogOptions.add(CUSTOM_STUDY_NEW);
+ dialogOptions.add(CUSTOM_STUDY_REV);
+ dialogOptions.add(CUSTOM_STUDY_FORGOT);
+ dialogOptions.add(CUSTOM_STUDY_AHEAD);
+ dialogOptions.add(CUSTOM_STUDY_RANDOM);
+ dialogOptions.add(CUSTOM_STUDY_PREVIEW);
+ dialogOptions.add(CUSTOM_STUDY_TAGS);
+ if (col.getSched().newCount() == 0) {
+ // If no new cards we wont show CUSTOM_STUDY_NEW
+ dialogOptions.remove(Integer.valueOf(CUSTOM_STUDY_NEW));
+ }
+ return ContextMenuHelper.integerListToArray(dialogOptions);
case CONTEXT_MENU_LIMITS:
// Special custom study options to show when the daily study limit has been reached
if (col.getSched().newDue() && col.getSched().revDue()) {
diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CustomStudyDialogTest.java b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CustomStudyDialogTest.java
index a2a3d931c413..624c02b6d435 100644
--- a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CustomStudyDialogTest.java
+++ b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CustomStudyDialogTest.java
@@ -22,13 +22,17 @@
import com.afollestad.materialdialogs.MaterialDialog;
import com.ichi2.anki.AnkiActivity;
import com.ichi2.anki.CardTemplateBrowserAppearanceEditor;
+import com.ichi2.anki.R;
import com.ichi2.anki.RobolectricTest;
import com.ichi2.anki.dialogs.CustomStudyDialog.CustomStudyListener;
import com.ichi2.libanki.Deck;
import com.ichi2.utils.JSONObject;
+import org.hamcrest.Matchers;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
import androidx.annotation.NonNull;
import androidx.fragment.app.testing.FragmentScenario;
@@ -71,6 +75,25 @@ public void learnAheadCardsRegressionTest() {
}
+ @Test
+ @Ignore("#8406")
+ @Config(qualifiers = "en")
+ public void increaseNewCardLimitRegressionTest(){
+ // #8338 - Regression Test
+ CustomStudyDialog standard = CustomStudyDialog.newInstance(CustomStudyDialog.CONTEXT_MENU_STANDARD, 1);
+
+ FragmentScenario scenarioStandard = getDialogScenario(standard);
+
+ scenarioStandard.moveToState(Lifecycle.State.STARTED);
+
+ scenarioStandard.onFragment(f -> {
+ MaterialDialog dialog = (MaterialDialog)f.getDialog();
+ assertThat(dialog,notNullValue());
+ assertThat(dialog.getItems(), Matchers.not(Matchers.hasItem(getResourceString(R.string.custom_study_increase_new_limit))));
+ });
+ }
+
+
@NonNull
private FragmentScenario getDialogScenario(CustomStudyDialog args) {
FragmentScenario scenario = FragmentScenario.launch(CustomStudyDialogForTesting.class, args.getArguments());
From 1a7102af2f726327f2bb99ea387bba79ef6965d1 Mon Sep 17 00:00:00 2001
From: Ben Hamrick <70414817+bhamrickatmills@users.noreply.github.com>
Date: Thu, 1 Apr 2021 15:37:57 -0700
Subject: [PATCH 024/171] Fix Login page Anki Icon Flicker (#8419)
* Fix condition to display anki logo
Co-authored-by: iserrano
---
.../main/java/com/ichi2/anki/MyAccount.java | 56 +++++--------------
1 file changed, 15 insertions(+), 41 deletions(-)
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/MyAccount.java b/AnkiDroid/src/main/java/com/ichi2/anki/MyAccount.java
index 110066e19ce7..4283fd4a912c 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/MyAccount.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/MyAccount.java
@@ -19,14 +19,14 @@
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Configuration;
-import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
+
+import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import android.view.KeyEvent;
-import android.view.OrientationEventListener;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
@@ -65,7 +65,6 @@ public class MyAccount extends AnkiActivity {
Toolbar mToolbar = null;
private TextInputLayout mPasswordLayout;
- private OrientationEventListener myOrientationEventListener;
private ImageView mAnkidroidLogo;
private void switchToState(int newState) {
@@ -117,8 +116,11 @@ protected void onCreate(Bundle savedInstanceState) {
switchToState(STATE_LOG_IN);
}
- // listener for handling future change in orientation
- initializeOrientationListener();
+ if (isScreenSmall() && this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ mAnkidroidLogo.setVisibility(View.GONE);
+ } else {
+ mAnkidroidLogo.setVisibility(View.VISIBLE);
+ }
}
@@ -319,49 +321,22 @@ protected String getHumanReadableLoginErrorMessage(Exception exception) {
return exception.getLocalizedMessage();
}
- public void handleCurrentOrientationState(int orientation) {
- if ((orientation == Configuration.ORIENTATION_UNDEFINED) || orientation == Configuration.ORIENTATION_LANDSCAPE) {
- mAnkidroidLogo.setVisibility(View.GONE);
- } else {
- mAnkidroidLogo.setVisibility(View.VISIBLE);
- }
+ private boolean isScreenSmall() {
+ return (this.getApplicationContext().getResources().getConfiguration().screenLayout
+ & Configuration.SCREENLAYOUT_SIZE_MASK)
+ < Configuration.SCREENLAYOUT_SIZE_LARGE;
}
- public void checkOrientation(int orientation) {
- // if device is in horizontal mode then screen might not have enough space for ankidroid logo
- // so we will invisible logo for horizontal mode only
- if ((orientation == OrientationEventListener.ORIENTATION_UNKNOWN) || (orientation < 100) || (orientation > 280)) {
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (isScreenSmall() && newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
mAnkidroidLogo.setVisibility(View.GONE);
} else {
mAnkidroidLogo.setVisibility(View.VISIBLE);
}
}
- public void initializeOrientationListener() {
- myOrientationEventListener = new OrientationEventListener(this, SensorManager.SENSOR_DELAY_NORMAL) {
- @Override
- public void onOrientationChanged(int orientation) {
- checkOrientation(orientation);
- }
- };
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- // handle current state
- handleCurrentOrientationState(getResources().getConfiguration().orientation);
- myOrientationEventListener.enable();
- }
-
-
- @Override
- protected void onPause() {
- super.onPause();
- myOrientationEventListener.disable();
- }
-
-
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
@@ -371,5 +346,4 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
}
return super.onKeyDown(keyCode, event);
}
-
}
From 3db30e871f040d0a1f146b4e39e56b2d78fb046d Mon Sep 17 00:00:00 2001
From: AnkiDroid Translations
Date: Thu, 1 Apr 2021 23:18:23 +0000
Subject: [PATCH 025/171] Updated strings from Crowdin
---
AnkiDroid/src/main/res/values-af/01-core.xml | 1 +
AnkiDroid/src/main/res/values-am/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ar/01-core.xml | 1 +
AnkiDroid/src/main/res/values-az/01-core.xml | 1 +
AnkiDroid/src/main/res/values-be/01-core.xml | 1 +
AnkiDroid/src/main/res/values-bg/01-core.xml | 1 +
AnkiDroid/src/main/res/values-bn/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ca/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ckb/01-core.xml | 1 +
AnkiDroid/src/main/res/values-cs/01-core.xml | 1 +
AnkiDroid/src/main/res/values-da/01-core.xml | 1 +
AnkiDroid/src/main/res/values-de/01-core.xml | 1 +
AnkiDroid/src/main/res/values-el/01-core.xml | 1 +
AnkiDroid/src/main/res/values-eo/01-core.xml | 1 +
.../src/main/res/values-es-rAR/01-core.xml | 1 +
.../src/main/res/values-es-rES/01-core.xml | 1 +
AnkiDroid/src/main/res/values-et/01-core.xml | 1 +
AnkiDroid/src/main/res/values-eu/01-core.xml | 1 +
AnkiDroid/src/main/res/values-fa/01-core.xml | 1 +
AnkiDroid/src/main/res/values-fi/01-core.xml | 1 +
AnkiDroid/src/main/res/values-fil/01-core.xml | 1 +
AnkiDroid/src/main/res/values-fr/01-core.xml | 1 +
AnkiDroid/src/main/res/values-fy/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ga/01-core.xml | 1 +
AnkiDroid/src/main/res/values-gl/01-core.xml | 1 +
AnkiDroid/src/main/res/values-got/01-core.xml | 1 +
AnkiDroid/src/main/res/values-gu/01-core.xml | 1 +
AnkiDroid/src/main/res/values-heb/01-core.xml | 1 +
AnkiDroid/src/main/res/values-hi/01-core.xml | 1 +
AnkiDroid/src/main/res/values-hr/01-core.xml | 1 +
AnkiDroid/src/main/res/values-hu/01-core.xml | 1 +
AnkiDroid/src/main/res/values-hy/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ind/01-core.xml | 1 +
AnkiDroid/src/main/res/values-is/01-core.xml | 1 +
AnkiDroid/src/main/res/values-it/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ja/01-core.xml | 1 +
AnkiDroid/src/main/res/values-jv/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ka/01-core.xml | 1 +
AnkiDroid/src/main/res/values-kk/01-core.xml | 1 +
AnkiDroid/src/main/res/values-km/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ko/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ku/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ky/01-core.xml | 1 +
AnkiDroid/src/main/res/values-lt/01-core.xml | 1 +
AnkiDroid/src/main/res/values-lv/01-core.xml | 1 +
AnkiDroid/src/main/res/values-mk/01-core.xml | 1 +
AnkiDroid/src/main/res/values-mn/01-core.xml | 1 +
AnkiDroid/src/main/res/values-mr/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ms/01-core.xml | 1 +
AnkiDroid/src/main/res/values-my/01-core.xml | 1 +
AnkiDroid/src/main/res/values-nl/01-core.xml | 1 +
AnkiDroid/src/main/res/values-nn/01-core.xml | 1 +
AnkiDroid/src/main/res/values-no/01-core.xml | 1 +
AnkiDroid/src/main/res/values-or/01-core.xml | 1 +
AnkiDroid/src/main/res/values-pa/01-core.xml | 1 +
AnkiDroid/src/main/res/values-pl/01-core.xml | 1 +
.../src/main/res/values-pt-rBR/01-core.xml | 1 +
.../src/main/res/values-pt-rPT/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ro/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ru/01-core.xml | 1 +
AnkiDroid/src/main/res/values-sat/01-core.xml | 1 +
AnkiDroid/src/main/res/values-sk/01-core.xml | 1 +
AnkiDroid/src/main/res/values-sl/01-core.xml | 1 +
AnkiDroid/src/main/res/values-sq/01-core.xml | 1 +
AnkiDroid/src/main/res/values-sr/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ss/01-core.xml | 1 +
AnkiDroid/src/main/res/values-sv/01-core.xml | 1 +
AnkiDroid/src/main/res/values-sw/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ta/01-core.xml | 1 +
AnkiDroid/src/main/res/values-te/01-core.xml | 1 +
AnkiDroid/src/main/res/values-tg/01-core.xml | 1 +
AnkiDroid/src/main/res/values-tgl/01-core.xml | 1 +
AnkiDroid/src/main/res/values-th/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ti/01-core.xml | 1 +
AnkiDroid/src/main/res/values-tn/01-core.xml | 1 +
AnkiDroid/src/main/res/values-tr/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ts/01-core.xml | 1 +
AnkiDroid/src/main/res/values-tt/01-core.xml | 9 ++--
.../src/main/res/values-tt/02-strings.xml | 4 +-
.../src/main/res/values-tt/03-dialogs.xml | 12 ++---
.../src/main/res/values-tt/04-network.xml | 8 ++--
.../src/main/res/values-tt/06-statistics.xml | 44 +++++++++----------
.../src/main/res/values-tt/07-cardbrowser.xml | 6 +--
.../src/main/res/values-tt/09-backup.xml | 2 +-
.../src/main/res/values-tt/10-preferences.xml | 26 +++++------
.../src/main/res/values-tt/11-arrays.xml | 8 ++--
.../res/values-tt/16-multimedia-editor.xml | 18 ++++----
.../main/res/values-tt/17-model-manager.xml | 2 +-
.../main/res/values-tt/18-standard-models.xml | 4 +-
AnkiDroid/src/main/res/values-uk/01-core.xml | 3 +-
AnkiDroid/src/main/res/values-ur/01-core.xml | 1 +
AnkiDroid/src/main/res/values-uz/01-core.xml | 1 +
AnkiDroid/src/main/res/values-ve/01-core.xml | 1 +
AnkiDroid/src/main/res/values-vi/01-core.xml | 1 +
AnkiDroid/src/main/res/values-wo/01-core.xml | 1 +
AnkiDroid/src/main/res/values-xh/01-core.xml | 1 +
AnkiDroid/src/main/res/values-yue/01-core.xml | 1 +
.../src/main/res/values-zh-rCN/01-core.xml | 1 +
.../src/main/res/values-zh-rTW/01-core.xml | 1 +
AnkiDroid/src/main/res/values-zu/01-core.xml | 1 +
.../marketdescription-tt-RU.txt | 2 +-
101 files changed, 162 insertions(+), 73 deletions(-)
diff --git a/AnkiDroid/src/main/res/values-af/01-core.xml b/AnkiDroid/src/main/res/values-af/01-core.xml
index db21f755b93b..4e4db5d0b79d 100644
--- a/AnkiDroid/src/main/res/values-af/01-core.xml
+++ b/AnkiDroid/src/main/res/values-af/01-core.xml
@@ -154,6 +154,7 @@
Pasmaak studie sessie
Hernoem die bestaande pasmaak kaartpak eers
Hierdie pak is leeg
+ Deck Search
Add card
Invalid deck name
Hierdie pak is leeg. Druk die + knoppie om nuwe inhoud by te voeg.
diff --git a/AnkiDroid/src/main/res/values-am/01-core.xml b/AnkiDroid/src/main/res/values-am/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-am/01-core.xml
+++ b/AnkiDroid/src/main/res/values-am/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-ar/01-core.xml b/AnkiDroid/src/main/res/values-ar/01-core.xml
index 3c7eab28beec..1ce950068b85 100644
--- a/AnkiDroid/src/main/res/values-ar/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ar/01-core.xml
@@ -174,6 +174,7 @@
جلسة دراسة مخصصة
أعد تسمية رزمة الدراسة المخصصة الموجودة أولًا
هذه الرزمة فارغة
+ Deck Search
إضافة بِطاقة
اسم رزمة غير صالح
هذه الرزمة فارغة. اضغط زر + لإضافة محتوى جديد.
diff --git a/AnkiDroid/src/main/res/values-az/01-core.xml b/AnkiDroid/src/main/res/values-az/01-core.xml
index 027904fc655c..9238fe47e76e 100644
--- a/AnkiDroid/src/main/res/values-az/01-core.xml
+++ b/AnkiDroid/src/main/res/values-az/01-core.xml
@@ -154,6 +154,7 @@
Şəxsi öyrənmə sessiyası
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-be/01-core.xml b/AnkiDroid/src/main/res/values-be/01-core.xml
index 9ca0486243bc..ba2041d840f7 100644
--- a/AnkiDroid/src/main/res/values-be/01-core.xml
+++ b/AnkiDroid/src/main/res/values-be/01-core.xml
@@ -164,6 +164,7 @@
Спецыяльная навучальная сесія
Спачатку дайце новую назву бягучай спецыяльнай калодзе
Гэта калода пустая
+ Deck Search
Add card
Invalid deck name
Гэта калода пустая. Націсніце кнопку +, каб дадаць новае змесціва.
diff --git a/AnkiDroid/src/main/res/values-bg/01-core.xml b/AnkiDroid/src/main/res/values-bg/01-core.xml
index 2cf5235ca2e0..dff26959107f 100644
--- a/AnkiDroid/src/main/res/values-bg/01-core.xml
+++ b/AnkiDroid/src/main/res/values-bg/01-core.xml
@@ -154,6 +154,7 @@
Допълнително време за учене
Първо преименувайте съществуващото тесте за учене
Това тесте е празно
+ Deck Search
Добавяне на карта
Невалидно име на тесте
Това тесте е празно. Натиснете бутона + за добавяне на ново съдържание.
diff --git a/AnkiDroid/src/main/res/values-bn/01-core.xml b/AnkiDroid/src/main/res/values-bn/01-core.xml
index 4dd93f276138..77be0c911484 100644
--- a/AnkiDroid/src/main/res/values-bn/01-core.xml
+++ b/AnkiDroid/src/main/res/values-bn/01-core.xml
@@ -154,6 +154,7 @@
নিজস্ব গবেষণা অধিবেশন
বর্তমান স্বনির্ধারিত গবেষণা ডেক প্রথম পুনঃনামকরণ করুন
এই ডেক ফাঁকা।
+ Deck Search
Add card
Invalid deck name
এই ডেক খালি আছে। প্রেস + বাটন নতুন উপাদান যোগ করতে।
diff --git a/AnkiDroid/src/main/res/values-ca/01-core.xml b/AnkiDroid/src/main/res/values-ca/01-core.xml
index 85e25123a5c1..382b5e7200df 100644
--- a/AnkiDroid/src/main/res/values-ca/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ca/01-core.xml
@@ -154,6 +154,7 @@
Sessió d\'estudi personalitzat
Si us plau, primer canvieu de nom el paquet d\'estudi a mida
Aquest paquet és buit
+ Deck Search
Add card
Invalid deck name
Aquest paquet és buit. Premeu el botó + per afegir nou contingut.
diff --git a/AnkiDroid/src/main/res/values-ckb/01-core.xml b/AnkiDroid/src/main/res/values-ckb/01-core.xml
index 9bb4bd28429e..98cb018e2c19 100644
--- a/AnkiDroid/src/main/res/values-ckb/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ckb/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-cs/01-core.xml b/AnkiDroid/src/main/res/values-cs/01-core.xml
index 6b731d716f70..569b5aaafbd6 100644
--- a/AnkiDroid/src/main/res/values-cs/01-core.xml
+++ b/AnkiDroid/src/main/res/values-cs/01-core.xml
@@ -164,6 +164,7 @@
Dodatečné procvičování
Nejdříve přejmenujte stávající balíček vlastního učení
Tento balíček je prázdný
+ Deck Search
Přidat kartu
Neplatný název balíčku
Tento balíček je prázdný. Stiskněte tlačítko +, abyste přidali nový obsah.
diff --git a/AnkiDroid/src/main/res/values-da/01-core.xml b/AnkiDroid/src/main/res/values-da/01-core.xml
index 686ef88bdfbb..61221196fd34 100644
--- a/AnkiDroid/src/main/res/values-da/01-core.xml
+++ b/AnkiDroid/src/main/res/values-da/01-core.xml
@@ -154,6 +154,7 @@
Tilpasset studiesession
Omdøb det eksisterende tilpassede dæk først
Dette kortsæt er tomt
+ Deck Search
Tilføj kort
Ugyldigt dæknavn
Dette dæk er tomt. Tryk på + knappen for at tilføje nyt indhold.
diff --git a/AnkiDroid/src/main/res/values-de/01-core.xml b/AnkiDroid/src/main/res/values-de/01-core.xml
index e0ffd5832e58..623161c68abe 100644
--- a/AnkiDroid/src/main/res/values-de/01-core.xml
+++ b/AnkiDroid/src/main/res/values-de/01-core.xml
@@ -154,6 +154,7 @@
Benutzerdefinierte Sitzung
Bitte benennen Sie zunächst den existierenden Stapel »Benutzerdefinierte Sitzung« um
Dieser Stapel ist leer
+ Deck Search
Karte hinzufügen
Ungültiger Stapelname
Dieser Stapel ist leer. Berühren Sie die »+«-Schaltfläche, um neue Inhalte hinzuzufügen.
diff --git a/AnkiDroid/src/main/res/values-el/01-core.xml b/AnkiDroid/src/main/res/values-el/01-core.xml
index 0eef1c5e2e66..22a48ca60345 100644
--- a/AnkiDroid/src/main/res/values-el/01-core.xml
+++ b/AnkiDroid/src/main/res/values-el/01-core.xml
@@ -154,6 +154,7 @@
Προσαρμοσμένη μελέτη λειτουργίας
Παρακαλούμε μετονομάστε την υπάρχουσα τράπουλα Προσαρμοσμένης Μελέτης πρώτα.
Αυτή η τράπουλα είναι κενή
+ Deck Search
Add card
Invalid deck name
Αυτή η τράπουλα είναι κενή. Πατήστε το κουμπί + για να προσθέσετε πριεχόμενο.
diff --git a/AnkiDroid/src/main/res/values-eo/01-core.xml b/AnkiDroid/src/main/res/values-eo/01-core.xml
index 0544ce4d57ba..1ed3bfd9f998 100644
--- a/AnkiDroid/src/main/res/values-eo/01-core.xml
+++ b/AnkiDroid/src/main/res/values-eo/01-core.xml
@@ -154,6 +154,7 @@
Propra lernado
Unue ŝanĝu nomon de kartaro por propra lernado
Tiu ĉi kartaro estas malplena
+ Deck Search
Aldoni karton
Malĝusta nomo por kartaro
Tiu ĉi kartaro estas malplena. Frapetu la butonon “+” por aldoni novan enhavon.
diff --git a/AnkiDroid/src/main/res/values-es-rAR/01-core.xml b/AnkiDroid/src/main/res/values-es-rAR/01-core.xml
index b6659d3e6071..59ef9e4acfa6 100644
--- a/AnkiDroid/src/main/res/values-es-rAR/01-core.xml
+++ b/AnkiDroid/src/main/res/values-es-rAR/01-core.xml
@@ -154,6 +154,7 @@
Sesión de estudio personalizado
Renombre primero el mazo de estudio personalizado existente
Este mazo está vacío
+ Deck Search
Añadir tarjeta
Nombre de mazo no válido
Este mazo está vacío. Pulse el botón + para añadir contenido nuevo.
diff --git a/AnkiDroid/src/main/res/values-es-rES/01-core.xml b/AnkiDroid/src/main/res/values-es-rES/01-core.xml
index 323c1c8785e5..d3ba3ff3e09c 100644
--- a/AnkiDroid/src/main/res/values-es-rES/01-core.xml
+++ b/AnkiDroid/src/main/res/values-es-rES/01-core.xml
@@ -154,6 +154,7 @@
Sesión de estudio personalizado
Renombra el mazo de estudio personalizado primero
Este mazo está vacío
+ Deck Search
Añadir tarjeta
Nombre de mazo no válido
Este mazo está vacío. Pulsa el botón + para añadir contenido nuevo.
diff --git a/AnkiDroid/src/main/res/values-et/01-core.xml b/AnkiDroid/src/main/res/values-et/01-core.xml
index 957aaebf8eb2..6a5e0a84bba1 100644
--- a/AnkiDroid/src/main/res/values-et/01-core.xml
+++ b/AnkiDroid/src/main/res/values-et/01-core.xml
@@ -154,6 +154,7 @@
Kohandatud õppesessioon
Esmalt nimeta ümber juba olemasolev täiendava õppe kaardipakk
See kaardipakk on tühi
+ Deck Search
Add card
Vigane kaardipaki nimi
See kaardipakk on tühi. Vajuta + nupule uue sisu lisamiseks.
diff --git a/AnkiDroid/src/main/res/values-eu/01-core.xml b/AnkiDroid/src/main/res/values-eu/01-core.xml
index 0c354cb8c5a7..e64b134ee056 100644
--- a/AnkiDroid/src/main/res/values-eu/01-core.xml
+++ b/AnkiDroid/src/main/res/values-eu/01-core.xml
@@ -154,6 +154,7 @@
Ikasketa saio pertsonalizatua
Aldatu izena dagoen ikasketa sortari aurretik
Sorta hau hutsik dago
+ Deck Search
Add card
Invalid deck name
Sorta hau hutsik dago. Sakatu + botoia edukia gehitzeko.
diff --git a/AnkiDroid/src/main/res/values-fa/01-core.xml b/AnkiDroid/src/main/res/values-fa/01-core.xml
index d8f88670bb68..43caa2fcc521 100644
--- a/AnkiDroid/src/main/res/values-fa/01-core.xml
+++ b/AnkiDroid/src/main/res/values-fa/01-core.xml
@@ -154,6 +154,7 @@
جلسه مطالعه سفارشی
لطفا ابتدا دسته مطالعه سفارشی موجود را مجددا نام گذاری کنید.
این دسته خالی است
+ Deck Search
اضافه کردن کارت
نام دسته نامعتبر است
این دسته خالی است. دکمه + را فشار دهید تا مطلب جدید اضافه کنید.
diff --git a/AnkiDroid/src/main/res/values-fi/01-core.xml b/AnkiDroid/src/main/res/values-fi/01-core.xml
index 88564bf2670f..4212218ad486 100644
--- a/AnkiDroid/src/main/res/values-fi/01-core.xml
+++ b/AnkiDroid/src/main/res/values-fi/01-core.xml
@@ -154,6 +154,7 @@
Mukautettu istunto
Nimeä olemassa oleva mukautettu pakka ensin
Tämä pakka on tyhjä
+ Deck Search
Add card
Pätemätön pakka nimi
Tämä pakka on tyhjä. Paina + painiketta lisätäksesi uutta sisältöä.
diff --git a/AnkiDroid/src/main/res/values-fil/01-core.xml b/AnkiDroid/src/main/res/values-fil/01-core.xml
index 869c73e81b2e..d81900e683f8 100644
--- a/AnkiDroid/src/main/res/values-fil/01-core.xml
+++ b/AnkiDroid/src/main/res/values-fil/01-core.xml
@@ -155,6 +155,7 @@ mga natitirang minuto
I-custom ang study session
Baguhin muna ang pangalan ng existing custom study deck
Ang deck na ito ay walang laman
+ Deck Search
Add card
Invalid deck name
Ang deck na ito ay walang laman. Pindotin ang + na button para magdagdag ng laman.
diff --git a/AnkiDroid/src/main/res/values-fr/01-core.xml b/AnkiDroid/src/main/res/values-fr/01-core.xml
index c92416f5f6f7..219e13ace7c6 100644
--- a/AnkiDroid/src/main/res/values-fr/01-core.xml
+++ b/AnkiDroid/src/main/res/values-fr/01-core.xml
@@ -154,6 +154,7 @@
Session d\'étude personnalisée
Merci de renommer d\'abord le paquet d\'étude personnalisée.
Le paquet est vide
+ Deck Search
Ajouter une carte
Nom de paquet invalide
Le paquet est vide. touchez le bouton + pour créer du contenu.
diff --git a/AnkiDroid/src/main/res/values-fy/01-core.xml b/AnkiDroid/src/main/res/values-fy/01-core.xml
index be0046f33053..ea11c6d70b85 100644
--- a/AnkiDroid/src/main/res/values-fy/01-core.xml
+++ b/AnkiDroid/src/main/res/values-fy/01-core.xml
@@ -154,6 +154,7 @@
Oanpaste stúdzjesessy
Hernoem de besteande stúdzjeset earst
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-ga/01-core.xml b/AnkiDroid/src/main/res/values-ga/01-core.xml
index 42193ba0cade..181623d9f204 100644
--- a/AnkiDroid/src/main/res/values-ga/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ga/01-core.xml
@@ -169,6 +169,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-gl/01-core.xml b/AnkiDroid/src/main/res/values-gl/01-core.xml
index b9da7b109ba5..ffc49676db06 100644
--- a/AnkiDroid/src/main/res/values-gl/01-core.xml
+++ b/AnkiDroid/src/main/res/values-gl/01-core.xml
@@ -154,6 +154,7 @@
Sesión de estudo personalizado
Por favor, renomee a baralla de estudo personalizado existente primeiro.
Esta baralla está baleira
+ Deck Search
Add card
Invalid deck name
Esta baralla está baleira. Preme no botón + para engadir contido novo.
diff --git a/AnkiDroid/src/main/res/values-got/01-core.xml b/AnkiDroid/src/main/res/values-got/01-core.xml
index 5830efaffe2a..76cfc1137a21 100644
--- a/AnkiDroid/src/main/res/values-got/01-core.xml
+++ b/AnkiDroid/src/main/res/values-got/01-core.xml
@@ -154,6 +154,7 @@
Anþara laiseinasaisjo
Gif þata frumist niujata namo du wisandon laiseinawikon
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-gu/01-core.xml b/AnkiDroid/src/main/res/values-gu/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-gu/01-core.xml
+++ b/AnkiDroid/src/main/res/values-gu/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-heb/01-core.xml b/AnkiDroid/src/main/res/values-heb/01-core.xml
index 1c833f182ef6..f0d9eaf007d1 100644
--- a/AnkiDroid/src/main/res/values-heb/01-core.xml
+++ b/AnkiDroid/src/main/res/values-heb/01-core.xml
@@ -165,6 +165,7 @@
שיעור למידה מותאם
ראשית, ערוך שם חפיסה חדשה
החפיסה ריקה
+ Deck Search
הוספת קלף
שם חפיסה לא חוקי
החפיסה ריקה. לחץ על כפתור ה+ להוספת תוכן חדש.
diff --git a/AnkiDroid/src/main/res/values-hi/01-core.xml b/AnkiDroid/src/main/res/values-hi/01-core.xml
index c53c1d54a092..ac2927a980fe 100644
--- a/AnkiDroid/src/main/res/values-hi/01-core.xml
+++ b/AnkiDroid/src/main/res/values-hi/01-core.xml
@@ -154,6 +154,7 @@
कस्टम अध्ययन सत्र
कृपया पहले मौजूदा कस्टम अध्ययन डेक का नाम बदलें।
इस डेक खाली है
+ Deck Search
कार्ड जोड़ें
अमान्य डेक नाम
यह डेक खाली है । नई सामग्री जोड़ने के लिए + बटन दबाएँ.
diff --git a/AnkiDroid/src/main/res/values-hr/01-core.xml b/AnkiDroid/src/main/res/values-hr/01-core.xml
index fe843bf40441..877a60aba290 100644
--- a/AnkiDroid/src/main/res/values-hr/01-core.xml
+++ b/AnkiDroid/src/main/res/values-hr/01-core.xml
@@ -159,6 +159,7 @@
Prilagođena sesija učenja
Prvo preimenujte postojeću prilagođenu skupinu za učenje
Ova skupina je prazna
+ Deck Search
Add card
Invalid deck name
Ova skupina je prazna. Pritisnite tipku + kako biste dodali novi sadržaj.
diff --git a/AnkiDroid/src/main/res/values-hu/01-core.xml b/AnkiDroid/src/main/res/values-hu/01-core.xml
index 5194a5ea034c..8681fa50e4fd 100644
--- a/AnkiDroid/src/main/res/values-hu/01-core.xml
+++ b/AnkiDroid/src/main/res/values-hu/01-core.xml
@@ -154,6 +154,7 @@
Egyéni tanulás pakli
Kérlek, előbb nevezd át a meglévő, Egyéni tanulás paklit
Ez a pakli üres
+ Deck Search
Kártya hozzáadása
Érvénytelen paklinév
Ez a pakli üres. Adj hozzá új tartalmat a \"+\" megnyomásával.
diff --git a/AnkiDroid/src/main/res/values-hy/01-core.xml b/AnkiDroid/src/main/res/values-hy/01-core.xml
index 1ad61ceef9a7..8bf4496df530 100644
--- a/AnkiDroid/src/main/res/values-hy/01-core.xml
+++ b/AnkiDroid/src/main/res/values-hy/01-core.xml
@@ -154,6 +154,7 @@
Ուսուցման լրացուցիչ աշխատաշրջան
Սկզբում վերանվանել լրացուցիչ ուսուցման առկա կապուկը
Այս կապուկը դատարկ է
+ Deck Search
Add card
Invalid deck name
Կապուկը դատարկ է: Սեղմեք + կոճակը նոր բովանդակություն ավելացնելու համար:
diff --git a/AnkiDroid/src/main/res/values-ind/01-core.xml b/AnkiDroid/src/main/res/values-ind/01-core.xml
index bbd631e58d5f..b0883f91784c 100644
--- a/AnkiDroid/src/main/res/values-ind/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ind/01-core.xml
@@ -149,6 +149,7 @@
Sesi belajar kustom
Ubah nama dek belajar kustom yang ada
Dek ini kosong
+ Deck Search
Add card
Nama dek tidak valid
Dek Ini kosong. Tekan tombol + untuk menambah konten baru.
diff --git a/AnkiDroid/src/main/res/values-is/01-core.xml b/AnkiDroid/src/main/res/values-is/01-core.xml
index 1ba9d3ad382e..0704f91409d3 100644
--- a/AnkiDroid/src/main/res/values-is/01-core.xml
+++ b/AnkiDroid/src/main/res/values-is/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-it/01-core.xml b/AnkiDroid/src/main/res/values-it/01-core.xml
index d0d9eb246d63..10254c78e0fc 100644
--- a/AnkiDroid/src/main/res/values-it/01-core.xml
+++ b/AnkiDroid/src/main/res/values-it/01-core.xml
@@ -154,6 +154,7 @@
Sessione di Studio Personalizzato
Rinomina dapprima il mazzo di Studio Personalizzato esistente.
Questo mazzo è vuoto
+ Deck Search
Aggiungi carta
Nome mazzo non valido
Questo mazzo è vuoto. Premi il pulsante + per aggiungere nuovi contenuti.
diff --git a/AnkiDroid/src/main/res/values-ja/01-core.xml b/AnkiDroid/src/main/res/values-ja/01-core.xml
index 67db16809b84..35888e44ea6a 100644
--- a/AnkiDroid/src/main/res/values-ja/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ja/01-core.xml
@@ -149,6 +149,7 @@
カスタム学習セッション
最初に、既存のカスタム学習デッキの名前を変更してください
このデッキにはカードがありません
+ Deck Search
カードの追加
無効なデッキ名です。
このデッキにはカードが入っていません。「+」ボタンをタップしてカードを追加してください。
diff --git a/AnkiDroid/src/main/res/values-jv/01-core.xml b/AnkiDroid/src/main/res/values-jv/01-core.xml
index 86c4c87b9863..4088b283a29d 100644
--- a/AnkiDroid/src/main/res/values-jv/01-core.xml
+++ b/AnkiDroid/src/main/res/values-jv/01-core.xml
@@ -149,6 +149,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-ka/01-core.xml b/AnkiDroid/src/main/res/values-ka/01-core.xml
index 618c57474fb0..d36b2f40cbf2 100644
--- a/AnkiDroid/src/main/res/values-ka/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ka/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-kk/01-core.xml b/AnkiDroid/src/main/res/values-kk/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-kk/01-core.xml
+++ b/AnkiDroid/src/main/res/values-kk/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-km/01-core.xml b/AnkiDroid/src/main/res/values-km/01-core.xml
index 16e46693fe77..c7dc3a70fbb5 100644
--- a/AnkiDroid/src/main/res/values-km/01-core.xml
+++ b/AnkiDroid/src/main/res/values-km/01-core.xml
@@ -149,6 +149,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-ko/01-core.xml b/AnkiDroid/src/main/res/values-ko/01-core.xml
index ef4abdc56624..3ca05d53f416 100644
--- a/AnkiDroid/src/main/res/values-ko/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ko/01-core.xml
@@ -149,6 +149,7 @@
맞춤 학습 세션
기존의 맞춤 학습 덱의 이름을 먼저 변경하세요.
이 카드 묶음은 비어있습니다.
+ Deck Search
카드追加
無效한 덱名입니다
이 카드 묶음은 비어있습니다. + 버튼을 눌러 새 컨텐츠를 추가하세요.
diff --git a/AnkiDroid/src/main/res/values-ku/01-core.xml b/AnkiDroid/src/main/res/values-ku/01-core.xml
index 9bb4bd28429e..98cb018e2c19 100644
--- a/AnkiDroid/src/main/res/values-ku/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ku/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-ky/01-core.xml b/AnkiDroid/src/main/res/values-ky/01-core.xml
index 15fec481d17d..5a933e8ed8d9 100644
--- a/AnkiDroid/src/main/res/values-ky/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ky/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-lt/01-core.xml b/AnkiDroid/src/main/res/values-lt/01-core.xml
index 7f627a952c2b..0afd7dce5d2d 100644
--- a/AnkiDroid/src/main/res/values-lt/01-core.xml
+++ b/AnkiDroid/src/main/res/values-lt/01-core.xml
@@ -164,6 +164,7 @@
Individualus mokymosi seansas
Prašome pirmiausiai pervadinti esamą individualaus mokymosi rinkinį
Šis rinkinys yra tuščias
+ Deck Search
Add card
Blogas rinkinio vardas
Šis rinkinys yra tuščias. Norėdami pridėti naują turinį, spauskite +.
diff --git a/AnkiDroid/src/main/res/values-lv/01-core.xml b/AnkiDroid/src/main/res/values-lv/01-core.xml
index 0a4bf2b89c48..f24b2c1cfbb0 100644
--- a/AnkiDroid/src/main/res/values-lv/01-core.xml
+++ b/AnkiDroid/src/main/res/values-lv/01-core.xml
@@ -159,6 +159,7 @@
Pielāgota pētījuma sesija
Lūdzu vispirms pārsauciet eksistējošo Pielāgoto pētījuma Deku.
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-mk/01-core.xml b/AnkiDroid/src/main/res/values-mk/01-core.xml
index 5fddabf8a4c2..96cd38f5a24e 100644
--- a/AnkiDroid/src/main/res/values-mk/01-core.xml
+++ b/AnkiDroid/src/main/res/values-mk/01-core.xml
@@ -154,6 +154,7 @@
Прилагодена студиска сесија
Прво преименувајте ја постоечката прилагодена студија на deck
Овој deck е празен
+ Deck Search
Add card
Невалидно име за шпил
Овој deck е празен. Притиснете го копчето + за да додадете нова содржина.
diff --git a/AnkiDroid/src/main/res/values-mn/01-core.xml b/AnkiDroid/src/main/res/values-mn/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-mn/01-core.xml
+++ b/AnkiDroid/src/main/res/values-mn/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-mr/01-core.xml b/AnkiDroid/src/main/res/values-mr/01-core.xml
index 1f373ae1e5d1..173b2e546e80 100644
--- a/AnkiDroid/src/main/res/values-mr/01-core.xml
+++ b/AnkiDroid/src/main/res/values-mr/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-ms/01-core.xml b/AnkiDroid/src/main/res/values-ms/01-core.xml
index 4a91136d16e1..27fe6c8920fb 100644
--- a/AnkiDroid/src/main/res/values-ms/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ms/01-core.xml
@@ -149,6 +149,7 @@
Adat belajar sesi
Menamakan ada adat belajar dek pertama
Dek ini adalah kosong
+ Deck Search
Add card
Invalid deck name
Dek ini adalah kosong. Tekan butang + untuk menambah kandungan baru.
diff --git a/AnkiDroid/src/main/res/values-my/01-core.xml b/AnkiDroid/src/main/res/values-my/01-core.xml
index 890622a3b6df..3e6aee4acb26 100644
--- a/AnkiDroid/src/main/res/values-my/01-core.xml
+++ b/AnkiDroid/src/main/res/values-my/01-core.xml
@@ -149,6 +149,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-nl/01-core.xml b/AnkiDroid/src/main/res/values-nl/01-core.xml
index c40893e4c324..fdf67c4219bf 100644
--- a/AnkiDroid/src/main/res/values-nl/01-core.xml
+++ b/AnkiDroid/src/main/res/values-nl/01-core.xml
@@ -154,6 +154,7 @@
Aangepaste studiesessie
Gelieve eerst de bestaande aangepaste studieset te hernoemen.
Deze leerset is leeg
+ Deck Search
Kaart toevoegen
Ongeldige setnaam
Deze leerset is leeg. Druk op de + knop om nieuwe inhoud toe te voegen.
diff --git a/AnkiDroid/src/main/res/values-nn/01-core.xml b/AnkiDroid/src/main/res/values-nn/01-core.xml
index 451332052d1b..e121a5bf23b4 100644
--- a/AnkiDroid/src/main/res/values-nn/01-core.xml
+++ b/AnkiDroid/src/main/res/values-nn/01-core.xml
@@ -154,6 +154,7 @@
Tilpasset studiesesjon
Gi den eksisterende tilpassede studiestokken nytt navn først
Denne kortstokken er tom
+ Deck Search
Add card
Invalid deck name
Denne kortstokken er tom. Bruk +-knappen for å legge til nytt innhold.
diff --git a/AnkiDroid/src/main/res/values-no/01-core.xml b/AnkiDroid/src/main/res/values-no/01-core.xml
index 451332052d1b..e121a5bf23b4 100644
--- a/AnkiDroid/src/main/res/values-no/01-core.xml
+++ b/AnkiDroid/src/main/res/values-no/01-core.xml
@@ -154,6 +154,7 @@
Tilpasset studiesesjon
Gi den eksisterende tilpassede studiestokken nytt navn først
Denne kortstokken er tom
+ Deck Search
Add card
Invalid deck name
Denne kortstokken er tom. Bruk +-knappen for å legge til nytt innhold.
diff --git a/AnkiDroid/src/main/res/values-or/01-core.xml b/AnkiDroid/src/main/res/values-or/01-core.xml
index f6d6361a588b..9831d7ecd0d1 100644
--- a/AnkiDroid/src/main/res/values-or/01-core.xml
+++ b/AnkiDroid/src/main/res/values-or/01-core.xml
@@ -154,6 +154,7 @@
ଇଚ୍ଛାରୂପିତ ଅଧ୍ୟୟନ ଅଧିବେଶନ
ଆଗ ସାମ୍ପ୍ରତିକ ଇଚ୍ଛାରୂପିତ ଅଧ୍ୟୟନାଧିବେଶନକୁ ପୁନଃନାମିତ କରନ୍ତୁ
ତାସଖଣ୍ଡଟି ଖାଲି ଅଛି
+ Deck Search
କାର୍ଡ ଯୋଡ଼ନ୍ତୁ
ଅବୈଧ ତାସଖଣ୍ଡ ନାମ
ଏହି ଡେକ୍ ଖାଲି ଅଛି। ନୂତନ ବିଷୟବସ୍ତୁ ଯୋଗ କରିବା ପାଇଁ + ବଟନ୍ ଦବାନ୍ତୁ।
diff --git a/AnkiDroid/src/main/res/values-pa/01-core.xml b/AnkiDroid/src/main/res/values-pa/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-pa/01-core.xml
+++ b/AnkiDroid/src/main/res/values-pa/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-pl/01-core.xml b/AnkiDroid/src/main/res/values-pl/01-core.xml
index 16ce8ee0776c..bfcb423958d8 100644
--- a/AnkiDroid/src/main/res/values-pl/01-core.xml
+++ b/AnkiDroid/src/main/res/values-pl/01-core.xml
@@ -164,6 +164,7 @@
Sesja nauki niestandardowej
Najpierw zmień nazwę istniejącej talii nauki niestandardowej.
Ta talia jest pusta
+ Deck Search
Dodaj kartę
Nieprawidłowa nazwa talii
Talia jest pusta. Wybierz przycisk + żeby dodać karty.
diff --git a/AnkiDroid/src/main/res/values-pt-rBR/01-core.xml b/AnkiDroid/src/main/res/values-pt-rBR/01-core.xml
index 62106807fa4a..8e3965d64306 100644
--- a/AnkiDroid/src/main/res/values-pt-rBR/01-core.xml
+++ b/AnkiDroid/src/main/res/values-pt-rBR/01-core.xml
@@ -154,6 +154,7 @@
Sessão de estudo personalizado
Por favor renomeie o baralho de estudo personalizado existente primeiro
Este baralho está vazio
+ Deck Search
Adicionar cartão
Nome do baralho inválido
Este baralho está vazio. Pressione o botão + para adicionar novos conteúdos.
diff --git a/AnkiDroid/src/main/res/values-pt-rPT/01-core.xml b/AnkiDroid/src/main/res/values-pt-rPT/01-core.xml
index 32eb5d4dc887..308dd19451e8 100644
--- a/AnkiDroid/src/main/res/values-pt-rPT/01-core.xml
+++ b/AnkiDroid/src/main/res/values-pt-rPT/01-core.xml
@@ -154,6 +154,7 @@
Sessão de estudo personalizado
Renomear primeiro o baralho de estudo personalizado existente
Este baralho está vazio
+ Deck Search
Add card
Invalid deck name
Este baralho está vazio. Toque no botão \"+\" para adicionar novo conteúdo.
diff --git a/AnkiDroid/src/main/res/values-ro/01-core.xml b/AnkiDroid/src/main/res/values-ro/01-core.xml
index 6c689e990233..11a8e028e35d 100644
--- a/AnkiDroid/src/main/res/values-ro/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ro/01-core.xml
@@ -159,6 +159,7 @@
Sesiune de studiu personalizata
Vă rugăm să redenumiţi mai inati pachetul de Studiu Personalizat .
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-ru/01-core.xml b/AnkiDroid/src/main/res/values-ru/01-core.xml
index 422d935b3756..cb09151c1d1a 100644
--- a/AnkiDroid/src/main/res/values-ru/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ru/01-core.xml
@@ -164,6 +164,7 @@
Дополнительная учебная сессия
Сначала переименуйте существующую дополнительную колоду
Эта колода пуста
+ Deck Search
Добавить карточку
Недопустимое название колоды
Эта колода пуста. Нажмите кнопку +, чтобы добавить что-нибудь.
diff --git a/AnkiDroid/src/main/res/values-sat/01-core.xml b/AnkiDroid/src/main/res/values-sat/01-core.xml
index a6679408c0d5..cf661605e9b6 100644
--- a/AnkiDroid/src/main/res/values-sat/01-core.xml
+++ b/AnkiDroid/src/main/res/values-sat/01-core.xml
@@ -154,6 +154,7 @@
ᱱᱤᱡᱮᱛᱮ ᱯᱟᱲᱦᱟᱣᱜ ᱥᱚᱢᱚᱭ ᱛᱮᱭᱟᱨ ᱢᱮ
ᱢᱟᱲᱟᱝ ᱠᱷᱚᱱ ᱢᱮᱱᱟᱜ ᱱᱤᱡᱮᱛᱮ ᱯᱟᱲᱦᱟᱣ ᱰᱮᱠ ᱟᱜ ᱧᱩᱛᱩᱢ ᱵᱚᱫᱚᱞ ᱢᱮ
ᱱᱚᱶᱟ ᱰᱮᱠ ᱫᱚ ᱠᱷᱟᱹᱞᱤ ᱜᱮᱭᱟ
+ Deck Search
ᱠᱟᱰ ᱥᱮᱞᱮᱫᱽ ᱢᱮ
ᱰᱮᱠ ᱧᱩᱛᱩᱢ ᱵᱷᱩᱞ
ᱱᱚᱶᱟ ᱰᱮᱠ ᱫᱚ ᱠᱷᱟᱹᱞᱤ ᱜᱮᱭᱟ ᱾ + ᱵᱚᱴᱚᱱ ᱯᱨᱮᱥ ᱠᱟᱛᱮ ᱱᱟᱶᱟ ᱡᱤᱱᱥᱚ ᱥᱮᱞᱮᱫ ᱪᱷᱚᱭ ᱢᱮ ᱾
diff --git a/AnkiDroid/src/main/res/values-sk/01-core.xml b/AnkiDroid/src/main/res/values-sk/01-core.xml
index a0618285cb2a..f124b9a98ce2 100644
--- a/AnkiDroid/src/main/res/values-sk/01-core.xml
+++ b/AnkiDroid/src/main/res/values-sk/01-core.xml
@@ -164,6 +164,7 @@
Vlastné štúdium
Prosím premenujte najprv existujúci balíček Vlastné štúdium.
Tento balíček je prázdny
+ Deck Search
Add card
Neplatný názov balíčka
Tento balíček je prázdny. Stlačte + pre pridávanie obsahu.
diff --git a/AnkiDroid/src/main/res/values-sl/01-core.xml b/AnkiDroid/src/main/res/values-sl/01-core.xml
index 5aba93843d4c..e631d6559fc1 100644
--- a/AnkiDroid/src/main/res/values-sl/01-core.xml
+++ b/AnkiDroid/src/main/res/values-sl/01-core.xml
@@ -164,6 +164,7 @@
Seja učenja po meri
Najprej preimenujte obstoječi komplet za učenje po meri.
Ta komplet je prazen
+ Deck Search
Add card
Invalid deck name
Ta komplet je prazen. Pritisnite na gumb +, da dodate novo vsebino.
diff --git a/AnkiDroid/src/main/res/values-sq/01-core.xml b/AnkiDroid/src/main/res/values-sq/01-core.xml
index f96c14f3c47c..78e1f7a76cec 100644
--- a/AnkiDroid/src/main/res/values-sq/01-core.xml
+++ b/AnkiDroid/src/main/res/values-sq/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-sr/01-core.xml b/AnkiDroid/src/main/res/values-sr/01-core.xml
index 80764c936032..f40c4fd5c174 100644
--- a/AnkiDroid/src/main/res/values-sr/01-core.xml
+++ b/AnkiDroid/src/main/res/values-sr/01-core.xml
@@ -159,6 +159,7 @@
Посебна сесија проучавања
Прво преименујте постојеће специјалне шпилове.
Овај шпил је празан
+ Deck Search
Додај карту
Invalid deck name
Овај шпил је празан. Притисните дугме +, да бисте додали нови садржај.
diff --git a/AnkiDroid/src/main/res/values-ss/01-core.xml b/AnkiDroid/src/main/res/values-ss/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-ss/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ss/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-sv/01-core.xml b/AnkiDroid/src/main/res/values-sv/01-core.xml
index 8507e6a90828..2a8f486d219f 100644
--- a/AnkiDroid/src/main/res/values-sv/01-core.xml
+++ b/AnkiDroid/src/main/res/values-sv/01-core.xml
@@ -154,6 +154,7 @@
Anpassad studiesession
Var god döp om den existerande anpassade studiekortleken först.
Den här leken är tom
+ Deck Search
Lägg till kort
Ogiltigt kortleksnamn
Den här leken är tom. Använd +-knappen för att lägga till nytt innehåll.
diff --git a/AnkiDroid/src/main/res/values-sw/01-core.xml b/AnkiDroid/src/main/res/values-sw/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-sw/01-core.xml
+++ b/AnkiDroid/src/main/res/values-sw/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-ta/01-core.xml b/AnkiDroid/src/main/res/values-ta/01-core.xml
index fc4e1c129280..6f54a7220b93 100644
--- a/AnkiDroid/src/main/res/values-ta/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ta/01-core.xml
@@ -154,6 +154,7 @@
தனிப்பயன் ஆய்வு கூட்டம்
தற்போதுள்ள விருப்ப ஆய்வு கட்டு முதலில் மறுபெயரிடு
இந்த கட்டு காலியாக உள்ளது
+ Deck Search
Add card
Invalid deck name
இந்த கட்டு காலியாக உள்ளது. பத்திரிகை, + புதிய உள்ளடக்கம் சேர்க்க பொத்தான்.
diff --git a/AnkiDroid/src/main/res/values-te/01-core.xml b/AnkiDroid/src/main/res/values-te/01-core.xml
index b3ffaf7c1b70..d0063f534efd 100644
--- a/AnkiDroid/src/main/res/values-te/01-core.xml
+++ b/AnkiDroid/src/main/res/values-te/01-core.xml
@@ -154,6 +154,7 @@
కస్టమ్ అధ్యయనం సెషన్
మొదటి సానుకూల అధ్యయనం డెక్ యొక్క పేరు మార్చండి
ఈ డెక్ ఖాళీగా ఉంది
+ Deck Search
Add card
Invalid deck name
ఈ డెక్ ఖాళీగా ఉంది. క్రొత్త కంటెంట్ని జోడించడానికి + బటన్ నొక్కండి.
diff --git a/AnkiDroid/src/main/res/values-tg/01-core.xml b/AnkiDroid/src/main/res/values-tg/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-tg/01-core.xml
+++ b/AnkiDroid/src/main/res/values-tg/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-tgl/01-core.xml b/AnkiDroid/src/main/res/values-tgl/01-core.xml
index c22e5f6ccc40..557f43283716 100644
--- a/AnkiDroid/src/main/res/values-tgl/01-core.xml
+++ b/AnkiDroid/src/main/res/values-tgl/01-core.xml
@@ -164,6 +164,7 @@ Pinagaralang %1$d mga baraha %2$s ngayong araw
Pinasadyang sesyon ng pagaaral
Palitan muna ang pangalan ng pinasadyang paaaral ng deck
Walang laman ang deck na ito
+ Deck Search
Add card
Invalid deck name
Walang laman ang deck na ito. Pindutin and + na butones upang magdagdag ng bago.
diff --git a/AnkiDroid/src/main/res/values-th/01-core.xml b/AnkiDroid/src/main/res/values-th/01-core.xml
index 207739f3061b..c154eee85aef 100644
--- a/AnkiDroid/src/main/res/values-th/01-core.xml
+++ b/AnkiDroid/src/main/res/values-th/01-core.xml
@@ -150,6 +150,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-ti/01-core.xml b/AnkiDroid/src/main/res/values-ti/01-core.xml
index 89e43901cb79..80311d6a979d 100644
--- a/AnkiDroid/src/main/res/values-ti/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ti/01-core.xml
@@ -154,6 +154,7 @@
መጽናዕቲ ብዝጠዕም ምምዕርራይ
መጀመርያ ዝነበረ ምምዕርራይ መጽናዕቲ ባይታ ከምባሓዱሽ ሰዩም።
እዚ ባይታ እዚ ጥርሑ እዩ ዘሎ።
+ Deck Search
Add card
Invalid deck name
ባይታ ጥርሑ ኣሎ። መልጎም + ጠውቅ ሓዲሽ ትሕዝቶ ክትውስኹ።
diff --git a/AnkiDroid/src/main/res/values-tn/01-core.xml b/AnkiDroid/src/main/res/values-tn/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-tn/01-core.xml
+++ b/AnkiDroid/src/main/res/values-tn/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-tr/01-core.xml b/AnkiDroid/src/main/res/values-tr/01-core.xml
index 73d2ae93c50a..f3a0d990c279 100644
--- a/AnkiDroid/src/main/res/values-tr/01-core.xml
+++ b/AnkiDroid/src/main/res/values-tr/01-core.xml
@@ -154,6 +154,7 @@
Kişiselleştirilmiş çalışma oturumu
Lütfen mevcut özelleştirilmiş çalışma destesini yeniden adlandırın.
Bu deste boş
+ Deck Search
Kart ekle
Geçersiz deste adı
Bu deste boş. Yeni içerik eklemek için + düğmesine basın.
diff --git a/AnkiDroid/src/main/res/values-ts/01-core.xml b/AnkiDroid/src/main/res/values-ts/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-ts/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ts/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-tt/01-core.xml b/AnkiDroid/src/main/res/values-tt/01-core.xml
index 37c3623c175b..88d6bd545ecc 100644
--- a/AnkiDroid/src/main/res/values-tt/01-core.xml
+++ b/AnkiDroid/src/main/res/values-tt/01-core.xml
@@ -104,10 +104,10 @@
Төс
Төс кую
Төссез
- Кызыл төс
- Кызыл-сары төс
- Яшел төс
- Зәңгәр төс
+ Кызыл байрак
+ Кызыл-сары байрак
+ Яшел байрак
+ Зәңгәр байрак
Тегларны үзгәртү
Бу язуны һәм бөтен аның кәртләрен бетерергәме?\n%s
Текстны сайлау
@@ -149,6 +149,7 @@
Шәхси өйрәнү сессиясе
Башта булган артык өйрәнү колода исемен үзгәртегез
Бу колода буш
+ Deck Search
Add card
Шундый колода исеме була алмый
Бу колода буш. Берәрнәрсә өстәү өчен + төймәсенә басыгыз.
diff --git a/AnkiDroid/src/main/res/values-tt/02-strings.xml b/AnkiDroid/src/main/res/values-tt/02-strings.xml
index 4b86f7083edd..7454146a5d21 100644
--- a/AnkiDroid/src/main/res/values-tt/02-strings.xml
+++ b/AnkiDroid/src/main/res/values-tt/02-strings.xml
@@ -222,10 +222,10 @@
- %s days
- - %s months
+ - %s ай
- - %s years
+ - %s ел
Disable
Overwrite
- Remove
+ Бетерү
Repair
Replace
Restore
- Set
+ Урнаштыру
Look up
The path specified wasn’t a valid directory
@@ -145,8 +145,8 @@
All cards
- New
- Due
+ Яңа
+ Вакыт
Finding empty cards…
Do you want to cancel?
diff --git a/AnkiDroid/src/main/res/values-tt/04-network.xml b/AnkiDroid/src/main/res/values-tt/04-network.xml
index cbc738948f8a..fa65810a659f 100644
--- a/AnkiDroid/src/main/res/values-tt/04-network.xml
+++ b/AnkiDroid/src/main/res/values-tt/04-network.xml
@@ -51,12 +51,12 @@
Email address
Password
- Log in
+ Керү
Don’t have an AnkiWeb account? It’s free!
Note: AnkiWeb is not affiliated with AnkiDroid
- Sign up
+ Теркәлү
Logged in as
- Log out
+ Чыгу
Logging in…
Invalid email address or password
Reset password
@@ -65,7 +65,7 @@
Options for XXX
You must either upload this collection or download it from Ankiweb, as they can’t be merged. The collection on one side will be overwritten.
Upload
- Download
+ Йөкләп алу
Overwrite your local collection?
Overwrite your collection on the server?
Preparing syncing…
diff --git a/AnkiDroid/src/main/res/values-tt/06-statistics.xml b/AnkiDroid/src/main/res/values-tt/06-statistics.xml
index 933d7a17240a..24927fe44369 100644
--- a/AnkiDroid/src/main/res/values-tt/06-statistics.xml
+++ b/AnkiDroid/src/main/res/values-tt/06-statistics.xml
@@ -57,15 +57,15 @@
Cumulative percentage
Cumulative correct %
Answers
- Minutes
- Hours
+ Минутлар
+ Сәгатьләр
Percentage
- 1 month
- 1 year
+ 1 ай
+ 1 ел
deck life
collection
Time of day
- Day of week
+ Атна көне
% correct
Reviews
Answer type
@@ -102,35 +102,35 @@
Highest ease: <b>%.0f%%</b>
Select timescale
- - Days
- - Weeks
- - Months
+ - Көннәр
+ - Атналар
+ - Айлар
- Today
+ Бүген
Overview
Forecast
Review count
Review time
- Added
+ Өстәлгән
Intervals
Hourly breakdown
Weekly breakdown
Answer buttons
- - 4AM
- - 10AM
- - 4PM
- - 10PM
- - 3AM
+ - 4:00
+ - 10:00
+ - 16:00
+ - 22:00
+ - 3:00
- - Sun
- - Mon
- - Tue
- - Wed
- - Thu
- - Fri
- - Sat
+ - Якш
+ - Дүш
+ - Сиш
+ - Чәр
+ - Сиш
+ - Җом
+ - Шим
- 1
diff --git a/AnkiDroid/src/main/res/values-tt/07-cardbrowser.xml b/AnkiDroid/src/main/res/values-tt/07-cardbrowser.xml
index a39b45157ac9..d04a3eaa6871 100644
--- a/AnkiDroid/src/main/res/values-tt/07-cardbrowser.xml
+++ b/AnkiDroid/src/main/res/values-tt/07-cardbrowser.xml
@@ -65,7 +65,7 @@
Change display order
Choose display order
Select a field twice to reverse
- Due
+ Вакыт
Tags
- No sorting (faster)
@@ -81,7 +81,7 @@
Select all
Select none
- (new)
+ (яңа)
(filtered)
(learning)
No cards found in deck ‘%s’
@@ -94,7 +94,7 @@
Added
First Review
Latest Review
- Interval
+ Интервал
Ease
Reviews
Lapses
diff --git a/AnkiDroid/src/main/res/values-tt/09-backup.xml b/AnkiDroid/src/main/res/values-tt/09-backup.xml
index 5bdd1159c286..f2bc25bd5fc8 100644
--- a/AnkiDroid/src/main/res/values-tt/09-backup.xml
+++ b/AnkiDroid/src/main/res/values-tt/09-backup.xml
@@ -47,7 +47,7 @@
Full sync from server
Overwrite your collection with the one from AnkiWeb? This will drop all your learning progress and added information since your last sync.
Error handling
- Options
+ Көйләүләр
Retry opening
Repair database
diff --git a/AnkiDroid/src/main/res/values-tt/10-preferences.xml b/AnkiDroid/src/main/res/values-tt/10-preferences.xml
index 1602926af8db..23e2350249c3 100644
--- a/AnkiDroid/src/main/res/values-tt/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values-tt/10-preferences.xml
@@ -51,8 +51,8 @@
Appearance
Change themes and default font
Themes
- Fonts
- Gestures
+ Шрифтлар
+ Ишарәләр
Use taps and swipes instead of buttons
Actions
Advanced
@@ -61,7 +61,7 @@
Workarounds
Plugins
About AnkiDroid
- Version:
+ Версия:
Stroke width
Type answer into the card
@@ -79,13 +79,13 @@
Display media filenames in the card browser question/answer fields
AnkiDroid directory
Fullscreen mode
- Off
+ Сүнгән
Hide the system bars
Hide the system bars and answer buttons
You must enable gestures to hide the answer buttons
Answer buttons position
- - Top
+ - Өске
- Bottom
Enable gestures
@@ -113,7 +113,7 @@
Touch bottom center
Touch bottom right
Volume up
- Volume down
+ Тавышны киметү
eReader
Also use kanji/katakana, emoji/kao-moji buttons for scrolling
‘%s’ Menu
@@ -134,10 +134,10 @@
Display synchronization status
Change the sync icon when changes can be uploaded
Day theme
- Night theme
+ Төнге режим
Default font
Default font applicability
- Language
+ Тел
System language
Notifications
Notify when
@@ -149,7 +149,7 @@
Automatic display answer
Show answer automatically without user input. Delay includes time for automatically played audio files.
Time to show answer
- XXX s
+ XXX сек
Time to show next question
Select language
Safe display mode
@@ -172,7 +172,7 @@
New card position
Learn ahead limit
Timebox time limit
- XXX min
+ XXX мин
New timezone handling
Start of next day (0 is midnight, 23 is 11PM)
XXX hours past midnight
@@ -227,7 +227,7 @@
General
Reminders
Steps (in minutes)
- Order
+ Тәртипкә салу
New cards/day
Graduating interval
XXX day(s)
@@ -261,7 +261,7 @@
Rename
Restore defaults
Restore current options group to default values?
- Add
+ Кушу
Create a copy of the current options group
Remove current options group?
Set for all subdecks
@@ -271,7 +271,7 @@
Set current options group for all of this deck’s subdecks?
Browser and editor font
Filter
- Search
+ Эзләү
Limit to
cards selected by
Reschedule
diff --git a/AnkiDroid/src/main/res/values-tt/11-arrays.xml b/AnkiDroid/src/main/res/values-tt/11-arrays.xml
index 636d468a5e97..d117edde212a 100644
--- a/AnkiDroid/src/main/res/values-tt/11-arrays.xml
+++ b/AnkiDroid/src/main/res/values-tt/11-arrays.xml
@@ -37,7 +37,7 @@
- Always report
- Never report
- - Ask me
+ - Миннән сорау
- Suspend card
@@ -72,12 +72,12 @@
- Always
- - Light
+ - Ак
- Plain
- - Black
- - Dark
+ - Кара
+ - Караңгы
- xx-small
diff --git a/AnkiDroid/src/main/res/values-tt/16-multimedia-editor.xml b/AnkiDroid/src/main/res/values-tt/16-multimedia-editor.xml
index 110759a6bf15..83e27679a190 100644
--- a/AnkiDroid/src/main/res/values-tt/16-multimedia-editor.xml
+++ b/AnkiDroid/src/main/res/values-tt/16-multimedia-editor.xml
@@ -46,7 +46,7 @@
From
To
- Translate
+ Тәрҗемә итү
Translating online
@@ -57,7 +57,7 @@
Install ColorDict first
- Load
+ Йөкләү
Looking up pronunciation
Pronunciation failed
@@ -65,8 +65,8 @@
Powered by Beolingus
- Text
- Image
+ Текст
+ Рәсем
Audio recording
Audio Clip
Done
@@ -74,9 +74,9 @@
Search for:
Pronunciation
- Clear
+ Чистарту
Clone from
- Translation
+ Тәрҗемә
Input some text first
Dictionary usually expects one word
@@ -88,8 +88,8 @@
Gallery
- Camera
- Library
+ Камера
+ Китапханә
Downloading
@@ -113,7 +113,7 @@
Unable to create recorder tool bar
Editing field
- Translation
+ Тәрҗемә
No connection
diff --git a/AnkiDroid/src/main/res/values-tt/17-model-manager.xml b/AnkiDroid/src/main/res/values-tt/17-model-manager.xml
index 521ddaa6c702..9938d3045551 100644
--- a/AnkiDroid/src/main/res/values-tt/17-model-manager.xml
+++ b/AnkiDroid/src/main/res/values-tt/17-model-manager.xml
@@ -55,7 +55,7 @@
Remember last input when adding
Updating fields
Sort by this field
- copy
+ күчереп алу
Are you sure you wish to delete this note type?
Are you sure you wish to delete this field?
diff --git a/AnkiDroid/src/main/res/values-tt/18-standard-models.xml b/AnkiDroid/src/main/res/values-tt/18-standard-models.xml
index 802df611f732..fe1e1af408ec 100644
--- a/AnkiDroid/src/main/res/values-tt/18-standard-models.xml
+++ b/AnkiDroid/src/main/res/values-tt/18-standard-models.xml
@@ -20,8 +20,8 @@
- Front
- Back
+ Алгы
+ Баш тарту
Text
Extra
Add Reverse
diff --git a/AnkiDroid/src/main/res/values-uk/01-core.xml b/AnkiDroid/src/main/res/values-uk/01-core.xml
index a8e8534faf6a..7b7ad0c73be3 100644
--- a/AnkiDroid/src/main/res/values-uk/01-core.xml
+++ b/AnkiDroid/src/main/res/values-uk/01-core.xml
@@ -116,7 +116,7 @@
Відкласти
Призупинити
Видалити запис
- Позначка
+ Прапорець
Позначити картку
Без позначки
Червоний прапорець
@@ -164,6 +164,7 @@
Додаткове навчання
Спочатку перейменуйте колоду додаткового навчання
Ця колода порожня
+ Deck Search
Додати картку
Некоректна назва колоди
Ця колода порожня. Натисніть на кнопку +, щоб додати новий вміст.
diff --git a/AnkiDroid/src/main/res/values-ur/01-core.xml b/AnkiDroid/src/main/res/values-ur/01-core.xml
index eb30e4ef6147..ad0bfe06a659 100644
--- a/AnkiDroid/src/main/res/values-ur/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ur/01-core.xml
@@ -154,6 +154,7 @@
اپنی مرضی کے مطابق تعلیمی سیشن
پہلے موجودہ مخصوص مطالعہ ڈیک کو نیا نام دیں
یہ ڈیک خالی ہے
+ Deck Search
Add card
Invalid deck name
یہ ڈیک خالی ہے ۔ + کے بٹن کو دبا کر نیا مواد ڈالیں.
diff --git a/AnkiDroid/src/main/res/values-uz/01-core.xml b/AnkiDroid/src/main/res/values-uz/01-core.xml
index f8ccf6a9c33b..653a32783b29 100644
--- a/AnkiDroid/src/main/res/values-uz/01-core.xml
+++ b/AnkiDroid/src/main/res/values-uz/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-ve/01-core.xml b/AnkiDroid/src/main/res/values-ve/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-ve/01-core.xml
+++ b/AnkiDroid/src/main/res/values-ve/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-vi/01-core.xml b/AnkiDroid/src/main/res/values-vi/01-core.xml
index 88a37ac89037..f2859fce345a 100644
--- a/AnkiDroid/src/main/res/values-vi/01-core.xml
+++ b/AnkiDroid/src/main/res/values-vi/01-core.xml
@@ -149,6 +149,7 @@
Phiên học tuỳ chọn
Đổi tên bộ thẻ học tuỳ chọn hiện hữu trước nhất
Bộ thẻ này rỗng
+ Deck Search
Thêm thẻ
Tên bộ thẻ không hợp lệ
Bộ thẻ này rỗng. Bấm bút + để thêm nội dung mới.
diff --git a/AnkiDroid/src/main/res/values-wo/01-core.xml b/AnkiDroid/src/main/res/values-wo/01-core.xml
index 86c4c87b9863..4088b283a29d 100644
--- a/AnkiDroid/src/main/res/values-wo/01-core.xml
+++ b/AnkiDroid/src/main/res/values-wo/01-core.xml
@@ -149,6 +149,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-xh/01-core.xml b/AnkiDroid/src/main/res/values-xh/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-xh/01-core.xml
+++ b/AnkiDroid/src/main/res/values-xh/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-yue/01-core.xml b/AnkiDroid/src/main/res/values-yue/01-core.xml
index 8083d07717fa..ae20a9067871 100644
--- a/AnkiDroid/src/main/res/values-yue/01-core.xml
+++ b/AnkiDroid/src/main/res/values-yue/01-core.xml
@@ -149,6 +149,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/AnkiDroid/src/main/res/values-zh-rCN/01-core.xml b/AnkiDroid/src/main/res/values-zh-rCN/01-core.xml
index af06402edebd..8477e0d1cb93 100644
--- a/AnkiDroid/src/main/res/values-zh-rCN/01-core.xml
+++ b/AnkiDroid/src/main/res/values-zh-rCN/01-core.xml
@@ -149,6 +149,7 @@
自定义学习的进程
请先重命名现有的自定义学习牌组。
此列表为空
+ Deck Search
添加卡片
无效的牌组名称
这个牌组是空的,请点击 + 按钮来添加内容。
diff --git a/AnkiDroid/src/main/res/values-zh-rTW/01-core.xml b/AnkiDroid/src/main/res/values-zh-rTW/01-core.xml
index e751dcc29a86..5e8d2841fefd 100644
--- a/AnkiDroid/src/main/res/values-zh-rTW/01-core.xml
+++ b/AnkiDroid/src/main/res/values-zh-rTW/01-core.xml
@@ -150,6 +150,7 @@
自訂學程
請先重新命名現有的「自訂學習」牌組。
這個牌組是空的
+ Deck Search
新增卡片
無效牌組名稱
這個牌組是空的。按下 + 按鈕增加新內容。
diff --git a/AnkiDroid/src/main/res/values-zu/01-core.xml b/AnkiDroid/src/main/res/values-zu/01-core.xml
index 2cdddc9f6a84..abd44cdb28e8 100644
--- a/AnkiDroid/src/main/res/values-zu/01-core.xml
+++ b/AnkiDroid/src/main/res/values-zu/01-core.xml
@@ -154,6 +154,7 @@
Custom study session
Rename the existing custom study deck first
This deck is empty
+ Deck Search
Add card
Invalid deck name
This deck is empty. Press the + button to add new content.
diff --git a/docs/marketing/localized_description/marketdescription-tt-RU.txt b/docs/marketing/localized_description/marketdescription-tt-RU.txt
index cadb8e016e64..479f9bcd9a43 100644
--- a/docs/marketing/localized_description/marketdescription-tt-RU.txt
+++ b/docs/marketing/localized_description/marketdescription-tt-RU.txt
@@ -16,7 +16,7 @@ Add material through the desktop application Anki or directly through AnkiDroid.
• progress widget
• detailed statistics
• syncing with AnkiWeb
-• open source
+• ачык код
★ Additional features:
• write answers (optional)
From 393a959a4406ce856f841cf4f6fb9c02294d0f68 Mon Sep 17 00:00:00 2001
From: Mike Hardy
Date: Fri, 2 Apr 2021 00:13:48 +0000
Subject: [PATCH 026/171] Bumped version to 2.15alpha38 @branch-specific
---
AnkiDroid/build.gradle | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/AnkiDroid/build.gradle b/AnkiDroid/build.gradle
index 4c74aab63904..591dab567a61 100644
--- a/AnkiDroid/build.gradle
+++ b/AnkiDroid/build.gradle
@@ -50,8 +50,8 @@ android {
//
// This ensures the correct ordering between the various types of releases (dev < alpha < beta < release) which is
// needed for upgrades to be offered correctly.
- versionCode=21500137
- versionName="2.15alpha37"
+ versionCode=21500138
+ versionName="2.15alpha38"
minSdkVersion 21
//noinspection OldTargetApi - also performed in api/build.fradle
targetSdkVersion 29 // change .travis.yml platform download at same time
From 952ad3d31ddd7a55ba23a4434b144c9ea7d862ea Mon Sep 17 00:00:00 2001
From: Piyush Goel <46752548+Arnold2381@users.noreply.github.com>
Date: Fri, 2 Apr 2021 21:12:04 +0530
Subject: [PATCH 027/171] lint: Enforce 'm' prefix rule for fields in
AbstractFlashcardViewer (#8435)
---
.../ichi2/anki/AbstractFlashcardViewer.java | 60 +++++++++----------
1 file changed, 30 insertions(+), 30 deletions(-)
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.java b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.java
index a40df5370302..69f50836de66 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.java
@@ -326,7 +326,7 @@ public abstract class AbstractFlashcardViewer extends NavigationDrawerActivity i
/**
* Swipe Detection
*/
- private GestureDetector gestureDetector;
+ private GestureDetector mGestureDetector;
private MyGestureDetector mGestureDetectorImpl;
private boolean mLinkOverridesTouchGesture;
@@ -369,7 +369,7 @@ public abstract class AbstractFlashcardViewer extends NavigationDrawerActivity i
* If we get 2 crashes on the same card, then we likely have an infinite loop and want to exit gracefully.
*/
@Nullable
- private Long lastCrashingCardId = null;
+ private Long mLastCrashingCardId = null;
/** Reference to the parent of the cardFrame to allow regeneration of the cardFrame in case of crash */
private ViewGroup mCardFrameParent;
@@ -399,8 +399,8 @@ public void handleMessage(Message msg) {
}
};
- private final Handler longClickHandler = new Handler();
- private final Runnable longClickTestRunnable = new Runnable() {
+ private final Handler mLongClickHandler = new Handler();
+ private final Runnable mLongClickTestRunnable = new Runnable() {
@Override
public void run() {
Timber.i("AbstractFlashcardViewer:: onEmulatedLongClick");
@@ -410,10 +410,10 @@ public void run() {
UIUtils.showThemedToast(AbstractFlashcardViewer.this, lookupHint, false);
}
CompatHelper.getCompat().vibrate(AnkiDroidApp.getInstance().getApplicationContext(), 50);
- longClickHandler.postDelayed(startLongClickAction, 300);
+ mLongClickHandler.postDelayed(mStartLongClickAction, 300);
}
};
- private final Runnable startLongClickAction = new Runnable() {
+ private final Runnable mStartLongClickAction = new Runnable() {
@Override
public void run() {
executeCommand(mGestureLongclick);
@@ -467,24 +467,24 @@ public void onClick(View view) {
private final View.OnTouchListener mGestureListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
- if (gestureDetector.onTouchEvent(event)) {
+ if (mGestureDetector.onTouchEvent(event)) {
return true;
}
if (!mDisableClipboard) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mTouchStarted = true;
- longClickHandler.postDelayed(longClickTestRunnable, 800);
+ mLongClickHandler.postDelayed(mLongClickTestRunnable, 800);
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_MOVE:
if (mTouchStarted) {
- longClickHandler.removeCallbacks(longClickTestRunnable);
+ mLongClickHandler.removeCallbacks(mLongClickTestRunnable);
mTouchStarted = false;
}
break;
default:
- longClickHandler.removeCallbacks(longClickTestRunnable);
+ mLongClickHandler.removeCallbacks(mLongClickTestRunnable);
mTouchStarted = false;
break;
}
@@ -857,7 +857,7 @@ protected String contentForCloze(String txt, int idx) {
private final Handler mTimerHandler = new Handler();
- private final Runnable removeChosenAnswerText = new Runnable() {
+ private final Runnable mRemoveChosenAnswerText = new Runnable() {
@Override
public void run() {
mChosenAnswer.setText("");
@@ -986,8 +986,8 @@ protected void onPause() {
mTimeoutHandler.removeCallbacks(mShowAnswerTask);
mTimeoutHandler.removeCallbacks(mShowQuestionTask);
- longClickHandler.removeCallbacks(longClickTestRunnable);
- longClickHandler.removeCallbacks(startLongClickAction);
+ mLongClickHandler.removeCallbacks(mLongClickTestRunnable);
+ mLongClickHandler.removeCallbacks(mStartLongClickAction);
pauseTimer();
mSoundPlayer.stopSounds();
@@ -1252,7 +1252,7 @@ private boolean hasLoadedCardContent() {
public GestureDetector getGestureDetector() {
- return gestureDetector;
+ return mGestureDetector;
}
@@ -1452,8 +1452,8 @@ protected void answerCard(@Consts.BUTTON_TYPE int ease) {
}
// remove chosen answer hint after a while
- mTimerHandler.removeCallbacks(removeChosenAnswerText);
- mTimerHandler.postDelayed(removeChosenAnswerText, sShowChosenAnswerLength);
+ mTimerHandler.removeCallbacks(mRemoveChosenAnswerText);
+ mTimerHandler.postDelayed(mRemoveChosenAnswerText, sShowChosenAnswerLength);
mSoundPlayer.stopSounds();
mCurrentEase = ease;
@@ -1508,7 +1508,7 @@ protected void initLayout() {
// Initialize swipe
mGestureDetectorImpl = mLinkOverridesTouchGesture ? new LinkDetectingGestureDetector() : new MyGestureDetector();
- gestureDetector = new GestureDetector(this, mGestureDetectorImpl);
+ mGestureDetector = new GestureDetector(this, mGestureDetectorImpl);
mEaseButtonsLayout = findViewById(R.id.ease_buttons);
@@ -1650,7 +1650,7 @@ private void flipOrAnswerCard(int cardOrdinal) {
private boolean webViewRendererLastCrashedOnCard(long cardId) {
- return lastCrashingCardId != null && lastCrashingCardId == cardId;
+ return mLastCrashingCardId != null && mLastCrashingCardId == cardId;
}
@@ -2856,9 +2856,9 @@ protected void closeReviewer(int result, boolean saveDeck) {
mTimeoutHandler.removeCallbacks(mShowAnswerTask);
mTimeoutHandler.removeCallbacks(mShowQuestionTask);
- mTimerHandler.removeCallbacks(removeChosenAnswerText);
- longClickHandler.removeCallbacks(longClickTestRunnable);
- longClickHandler.removeCallbacks(startLongClickAction);
+ mTimerHandler.removeCallbacks(mRemoveChosenAnswerText);
+ mLongClickHandler.removeCallbacks(mLongClickTestRunnable);
+ mLongClickHandler.removeCallbacks(mStartLongClickAction);
AbstractFlashcardViewer.this.setResult(result);
@@ -2896,12 +2896,12 @@ protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) {
super.onScrollChanged(horiz, vert, oldHoriz, oldVert);
if (Math.abs(horiz - oldHoriz) > Math.abs(vert - oldVert)) {
mIsXScrolling = true;
- scrollHandler.removeCallbacks(scrollXRunnable);
- scrollHandler.postDelayed(scrollXRunnable, 300);
+ mScrollHandler.removeCallbacks(mScrollXRunnable);
+ mScrollHandler.postDelayed(mScrollXRunnable, 300);
} else {
mIsYScrolling = true;
- scrollHandler.removeCallbacks(scrollYRunnable);
- scrollHandler.postDelayed(scrollYRunnable, 300);
+ mScrollHandler.removeCallbacks(mScrollYRunnable);
+ mScrollHandler.postDelayed(mScrollYRunnable, 300);
}
}
@@ -2942,9 +2942,9 @@ private ViewParent findScrollParent(View current) {
return null;
}
- private final Handler scrollHandler = new Handler();
- private final Runnable scrollXRunnable = () -> mIsXScrolling = false;
- private final Runnable scrollYRunnable = () -> mIsYScrolling = false;
+ private final Handler mScrollHandler = new Handler();
+ private final Runnable mScrollXRunnable = () -> mIsXScrolling = false;
+ private final Runnable mScrollYRunnable = () -> mIsYScrolling = false;
}
@@ -3027,7 +3027,7 @@ public boolean onDoubleTap(MotionEvent e) {
@Override
public boolean onSingleTapUp(MotionEvent e) {
if (mTouchStarted) {
- longClickHandler.removeCallbacks(longClickTestRunnable);
+ mLongClickHandler.removeCallbacks(mLongClickTestRunnable);
mTouchStarted = false;
}
return false;
@@ -3700,7 +3700,7 @@ public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail)
// If we get here, the error is non-fatal and we should re-render the WebView
// This logic may need to be better defined. The card could have changed by the time we get here.
- lastCrashingCardId = mCurrentCard.getId();
+ mLastCrashingCardId = mCurrentCard.getId();
String nonFatalError = getResources().getString(R.string.webview_crash_nonfatal, errorCauseString);
From c91dd1793abe8032ff65f16cc884ab784b1d330b Mon Sep 17 00:00:00 2001
From: Vaibhavi Lokegaonkar <53178543+Vaibhavi1707@users.noreply.github.com>
Date: Sat, 3 Apr 2021 00:27:33 +0530
Subject: [PATCH 028/171] Avoid returning 0 SQL window size accidentally
(#8433)
* SQL window size calculation fixed
Co-authored-by: Mike Hardy
---
AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
index 1fbcef3c9669..83c0999e42f7 100644
--- a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
+++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
@@ -332,10 +332,8 @@ private int getChunk() {
// We have the ability to look into our sqlite implementation on Android and use it's value
// as a ceiling. Try it, with a reasonable fallback in case of failure
SupportSQLiteDatabase db = mDb.getDatabase();
- if (! (db instanceof DatabaseChangeDecorator)) {
- return sChunk;
- }
- String db_name = ((DatabaseChangeDecorator) db).getWrapped().getClass().getName();
+ String db_name = (db instanceof DatabaseChangeDecorator) ? ((DatabaseChangeDecorator) db).getWrapped().getClass().getName() : null;
+
if ("io.requery.android.database.sqlite.SQLiteDatabase".equals(db_name)) {
try {
Field cursorWindowSize = io.requery.android.database.CursorWindow.class.getDeclaredField("sDefaultCursorWindowSize");
@@ -353,6 +351,7 @@ private int getChunk() {
}
// reduce the actual size a little bit.
+ // In case db is not an instance of DatabaseChangeDecorator, sChunk evaluated on default window size
sChunk = (int) (sCursorWindowSize * 15. / 16.);
return sChunk;
}
From a18fc25f8babb93ba73abcd9f30c6ab172793026 Mon Sep 17 00:00:00 2001
From: Arthur Milchior
Date: Wed, 31 Mar 2021 01:46:22 +0200
Subject: [PATCH 029/171] NF: Split Dismiss task
This avoid a huge case that is not even exhaustive, far more readable as each class concentrate on what matters
---
.../ichi2/anki/AbstractFlashcardViewer.java | 15 +-
.../main/java/com/ichi2/anki/Reviewer.java | 12 +-
.../java/com/ichi2/async/CollectionTask.java | 144 +++++++++++-------
.../ichi2/anki/ReviewerKeyboardInputTest.java | 13 +-
4 files changed, 109 insertions(+), 75 deletions(-)
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.java b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.java
index 69f50836de66..43fc15711a09 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.java
@@ -148,7 +148,6 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.locks.Lock;
@@ -1387,7 +1386,7 @@ protected void showDeleteNoteDialog() {
.onPositive((dialog, which) -> {
Timber.i("AbstractFlashcardViewer:: OK button pressed to delete note %d", mCurrentCard.getNid());
mSoundPlayer.stopSounds();
- dismiss(Collection.DismissType.DELETE_NOTE);
+ dismiss(new CollectionTask.DeleteNote(mCurrentCard));
})
.build().show();
}
@@ -2635,16 +2634,16 @@ public boolean executeCommand(@ViewerCommandDef int which) {
lookUpOrSelectText();
return true;
case COMMAND_BURY_CARD:
- dismiss(Collection.DismissType.BURY_CARD);
+ dismiss(new CollectionTask.BuryCard(mCurrentCard));
return true;
case COMMAND_BURY_NOTE:
- dismiss(Collection.DismissType.BURY_NOTE);
+ dismiss(new CollectionTask.BuryNote(mCurrentCard));
return true;
case COMMAND_SUSPEND_CARD:
- dismiss(Collection.DismissType.SUSPEND_CARD);
+ dismiss(new CollectionTask.SuspendCard(mCurrentCard));
return true;
case COMMAND_SUSPEND_NOTE:
- dismiss(Collection.DismissType.SUSPEND_NOTE);
+ dismiss(new CollectionTask.SuspendNote(mCurrentCard));
return true;
case COMMAND_DELETE:
showDeleteNoteDialog();
@@ -3277,9 +3276,9 @@ protected void onFlag(Card card, @FlagDef int flag) {
} */
}
- protected void dismiss(Collection.DismissType type) {
+ protected void dismiss(CollectionTask.DismissNote dismiss) {
blockControls(false);
- TaskManager.launchCollectionTask(new CollectionTask.DismissNote(mCurrentCard, type), mDismissCardHandler);
+ TaskManager.launchCollectionTask(dismiss, mDismissCardHandler);
}
/** Signals from a WebView represent actions with no parameters */
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.java b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.java
index b3722c7f3906..3991fe872f40 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.java
@@ -342,13 +342,13 @@ public boolean onOptionsItemSelected(MenuItem item) {
Timber.i("Reviewer:: Bury button pressed");
if (!MenuItemCompat.getActionProvider(item).hasSubMenu()) {
Timber.d("Bury card due to no submenu");
- dismiss(DismissType.BURY_CARD);
+ dismiss(new CollectionTask.BuryCard(mCurrentCard));
}
} else if (itemId == R.id.action_suspend) {
Timber.i("Reviewer:: Suspend button pressed");
if (!MenuItemCompat.getActionProvider(item).hasSubMenu()) {
Timber.d("Suspend card due to no submenu");
- dismiss(DismissType.SUSPEND_CARD);
+ dismiss(new CollectionTask.SuspendCard(mCurrentCard));
}
} else if (itemId == R.id.action_delete) {
Timber.i("Reviewer:: Delete note button pressed");
@@ -1306,10 +1306,10 @@ public void onPrepareSubMenu(SubMenu subMenu) {
public boolean onMenuItemClick(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.action_suspend_card) {
- dismiss(DismissType.SUSPEND_CARD);
+ dismiss(new CollectionTask.SuspendCard(mCurrentCard));
return true;
} else if (itemId == R.id.action_suspend_note) {
- dismiss(DismissType.SUSPEND_NOTE);
+ dismiss(new CollectionTask.SuspendNote(mCurrentCard));
return true;
}
return false;
@@ -1354,10 +1354,10 @@ public void onPrepareSubMenu(SubMenu subMenu) {
public boolean onMenuItemClick(MenuItem item) {
int itemId = item.getItemId();
if (itemId == R.id.action_bury_card) {
- dismiss(DismissType.BURY_CARD);
+ dismiss(new CollectionTask.BuryCard(mCurrentCard));
return true;
} else if (itemId == R.id.action_bury_note) {
- dismiss(DismissType.BURY_NOTE);
+ dismiss(new CollectionTask.BuryNote(mCurrentCard));
return true;
}
return false;
diff --git a/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java b/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
index a763c7670500..e46316b62ea8 100644
--- a/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
+++ b/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
@@ -478,75 +478,27 @@ public UndoDeleteNote(Note note, ArrayList allCs, @NonNull Card card) {
}
}
-
- public static class DismissNote extends Task {
+ public static abstract class DismissNote extends Task {
private final Card card;
- private final Collection.DismissType type;
- public DismissNote(Card card, Collection.DismissType type) {
+ public DismissNote(Card card) {
this.card = card;
- this.type = type;
}
+ protected abstract void actualTask(Collection col, Card card);
+
protected BooleanGetter task(Collection col, ProgressSenderAndCancelListener collectionTask) {
- AbstractSched sched = col.getSched();
- Note note = card.note();
try {
col.getDb().executeInTransaction(() -> {
- sched.deferReset();
- switch (type) {
- case BURY_CARD:
- // collect undo information
- col.markUndo(revertToProvidedState(BURY_CARD, card));
- // then bury
- sched.buryCards(new long[] {card.getId()});
- break;
- case BURY_NOTE:
- // collect undo information
- col.markUndo(revertToProvidedState(BURY_NOTE, card));
- // then bury
- sched.buryNote(note.getId());
- break;
- case SUSPEND_CARD:
- // collect undo information
- Card suspendedCard = card.clone();
- col.markUndo(new UndoSuspendCard(suspendedCard));
- // suspend card
- if (card.getQueue() == Consts.QUEUE_TYPE_SUSPENDED) {
- sched.unsuspendCards(new long[] {card.getId()});
- } else {
- sched.suspendCards(new long[] {card.getId()});
- }
- break;
- case SUSPEND_NOTE: {
- // collect undo information
- ArrayList cards = note.cards();
- long[] cids = new long[cards.size()];
- for (int i = 0; i < cards.size(); i++) {
- cids[i] = cards.get(i).getId();
- }
- col.markUndo(revertToProvidedState(SUSPEND_NOTE, card));
- // suspend note
- sched.suspendCards(cids);
- break;
- }
-
- case DELETE_NOTE: {
- // collect undo information
- ArrayList allCs = note.cards();
- col.markUndo(new UndoDeleteNote(note, allCs, card));
- // delete note
- col.remNotes(new long[] {note.getId()});
- break;
- }
- }
+ col.getSched().deferReset();
+ actualTask(col, card);
// With sHadCardQueue set, getCard() resets the scheduler prior to getting the next card
collectionTask.doProgress(col.getSched().getCard());
});
} catch (RuntimeException e) {
- Timber.e(e, "doInBackgroundDismissNote - RuntimeException on dismissing note, dismiss type %s", type);
+ Timber.e(e, "doInBackgroundDismissNote - RuntimeException on dismissing note, dismiss type %s", this.getClass());
AnkiDroidApp.sendExceptionReport(e, "doInBackgroundDismissNote");
return False;
}
@@ -554,6 +506,88 @@ protected BooleanGetter task(Collection col, ProgressSenderAndCancelListener cards = card.note().cards();
+ long[] cids = new long[cards.size()];
+ for (int i = 0; i < cards.size(); i++) {
+ cids[i] = cards.get(i).getId();
+ }
+ col.markUndo(revertToProvidedState(SUSPEND_NOTE, card));
+ // suspend note
+ col.getSched().suspendCards(cids);
+ }
+ }
+
+ public static class DeleteNote extends DismissNote {
+ public DeleteNote(Card card) {
+ super(card);
+ }
+
+ @Override
+ protected void actualTask(Collection col, Card card) {
+ Note note = card.note();
+ // collect undo information
+ ArrayList allCs = note.cards();
+ col.markUndo(new UndoDeleteNote(note, allCs, card));
+ // delete note
+ col.remNotes(new long[] {note.getId()});
+ }
+ }
+
protected static class UndoSuspendCardMulti extends Undoable {
private final Card[] cards;
diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ReviewerKeyboardInputTest.java b/AnkiDroid/src/test/java/com/ichi2/anki/ReviewerKeyboardInputTest.java
index 15b5280a7508..1cf5198ced69 100644
--- a/AnkiDroid/src/test/java/com/ichi2/anki/ReviewerKeyboardInputTest.java
+++ b/AnkiDroid/src/test/java/com/ichi2/anki/ReviewerKeyboardInputTest.java
@@ -19,6 +19,7 @@
import android.view.KeyEvent;
import com.ichi2.anki.reviewer.ReviewerUi;
+import com.ichi2.async.CollectionTask;
import com.ichi2.libanki.Card;
import com.ichi2.libanki.Collection;
@@ -271,7 +272,7 @@ static class KeyboardInputTestReviewer extends Reviewer {
private int mAnswerButtonCount = 4;
private boolean mEditedCard;
private boolean mMarkedCard;
- private Collection.DismissType mDismissType;
+ private CollectionTask.DismissNote mDismissType;
private boolean mUndoCalled;
private boolean mReplayAudioCalled;
private ControlBlock mControlsAreBlocked = ControlBlock.UNBLOCKED;
@@ -454,12 +455,12 @@ public boolean getUndoCalled() {
}
public boolean getSuspendNoteCalled() {
- return mDismissType == Collection.DismissType.SUSPEND_NOTE;
+ return mDismissType instanceof CollectionTask.SuspendNote;
}
public boolean getBuryNoteCalled() {
- return mDismissType == Collection.DismissType.BURY_NOTE;
+ return mDismissType instanceof CollectionTask.BuryNote;
}
@@ -473,8 +474,8 @@ public boolean getEditCardCalled() {
}
@Override
- protected void dismiss(Collection.DismissType type) {
- this.mDismissType = type;
+ protected void dismiss(CollectionTask.DismissNote dismiss) {
+ this.mDismissType = dismiss;
}
@Override
@@ -489,7 +490,7 @@ protected void onMark(Card card) {
public boolean getSuspendCardCalled() {
- return mDismissType == Collection.DismissType.SUSPEND_CARD;
+ return mDismissType instanceof CollectionTask.SuspendCard;
}
From 91f539adf01ff8bee605c86508abe630b8370977 Mon Sep 17 00:00:00 2001
From: Arthur Milchior
Date: Wed, 31 Mar 2021 02:08:15 +0200
Subject: [PATCH 030/171] NF: class UndoReview
In next commit, it will be useful since we'll use Undoable's class for test
---
.../java/com/ichi2/libanki/Collection.java | 25 +++++++++++++------
1 file changed, 18 insertions(+), 7 deletions(-)
diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
index 83c0999e42f7..636e7688bd62 100644
--- a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
+++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
@@ -1383,16 +1383,27 @@ public void markUndo(@NonNull Undoable undo) {
}
}
+ private static class UndoReview extends Undoable {
+ private final boolean mWasLeech;
+ @NonNull private final Card mClonedCard;
+ public UndoReview(boolean wasLeech, @NonNull Card clonedCard) {
+ super(REVIEW);
+ mClonedCard = clonedCard;
+ mWasLeech = wasLeech;
+ }
+
+ @NonNull
+ @Override
+ public Card undo(@NonNull Collection col) {
+ col.getSched().undoReview(mClonedCard, mWasLeech);
+ return mClonedCard;
+ }
+ }
+
public void markReview(Card card) {
boolean wasLeech = card.note().hasTag("leech");
Card clonedCard = card.clone();
- Undoable undoableReview = new Undoable(REVIEW) {
- public @Nullable Card undo(@NonNull Collection col) {
- col.getSched().undoReview(clonedCard, wasLeech);
- return clonedCard;
- }
- };
- markUndo(undoableReview);
+ markUndo(new UndoReview(wasLeech, clonedCard));
}
/**
From 48eaec826892b940182fb6bdee7a53c2d44ea53e Mon Sep 17 00:00:00 2001
From: Arthur Milchior
Date: Wed, 31 Mar 2021 02:00:05 +0200
Subject: [PATCH 031/171] NF: remove the enum DismissType
It was redundant with Undoable
---
.../main/java/com/ichi2/anki/Reviewer.java | 1 -
.../java/com/ichi2/async/CollectionTask.java | 49 ++++++++----------
.../java/com/ichi2/libanki/Collection.java | 51 ++++---------------
.../main/java/com/ichi2/libanki/Undoable.java | 25 ++++-----
.../test/java/com/ichi2/libanki/UndoTest.java | 4 +-
5 files changed, 47 insertions(+), 83 deletions(-)
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.java b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.java
index 3991fe872f40..5b9d59aeaffb 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.java
@@ -76,7 +76,6 @@
import com.ichi2.compat.CompatHelper;
import com.ichi2.libanki.Card;
import com.ichi2.libanki.Collection;
-import com.ichi2.libanki.Collection.DismissType;
import com.ichi2.libanki.Consts;
import com.ichi2.libanki.Decks;
import com.ichi2.libanki.Utils;
diff --git a/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java b/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
index e46316b62ea8..4afbcbd19cec 100644
--- a/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
+++ b/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
@@ -87,17 +87,12 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting;
import timber.log.Timber;
import static com.ichi2.async.TaskManager.setLatestInstance;
import static com.ichi2.libanki.Card.deepCopyCardArray;
-import static com.ichi2.libanki.Collection.DismissType.BURY_CARD;
-import static com.ichi2.libanki.Collection.DismissType.BURY_NOTE;
-import static com.ichi2.libanki.Collection.DismissType.REPOSITION_CARDS;
-import static com.ichi2.libanki.Collection.DismissType.RESCHEDULE_CARDS;
-import static com.ichi2.libanki.Collection.DismissType.RESET_CARDS;
-import static com.ichi2.libanki.Collection.DismissType.SUSPEND_NOTE;
import static com.ichi2.libanki.Undoable.*;
import static com.ichi2.utils.BooleanGetter.False;
import static com.ichi2.utils.BooleanGetter.True;
@@ -437,7 +432,7 @@ private static class UndoSuspendCard extends Undoable {
public UndoSuspendCard(Card suspendedCard) {
- super(Collection.DismissType.SUSPEND_CARD);
+ super(R.string.menu_suspend_card);
this.suspendedCard = suspendedCard;
}
@@ -457,7 +452,7 @@ private static class UndoDeleteNote extends Undoable {
public UndoDeleteNote(Note note, ArrayList allCs, @NonNull Card card) {
- super(Collection.DismissType.DELETE_NOTE);
+ super(R.string.menu_delete_note);
this.note = note;
this.allCs = allCs;
this.card = card;
@@ -514,7 +509,7 @@ public BuryCard(Card card) {
@Override
protected void actualTask(Collection col, Card card) {
// collect undo information
- col.markUndo(revertToProvidedState(BURY_CARD, card));
+ col.markUndo(revertToProvidedState(R.string.menu_bury_card, card));
// then bury
col.getSched().buryCards(new long[] {card.getId()});
}
@@ -528,7 +523,7 @@ public BuryNote(Card card) {
@Override
protected void actualTask(Collection col, Card card) {
// collect undo information
- col.markUndo(revertToProvidedState(BURY_NOTE, card));
+ col.markUndo(revertToProvidedState(R.string.menu_bury_note, card));
// then bury
col.getSched().buryNote(card.note().getId());
}
@@ -566,7 +561,7 @@ protected void actualTask(Collection col, Card card) {
for (int i = 0; i < cards.size(); i++) {
cids[i] = cards.get(i).getId();
}
- col.markUndo(revertToProvidedState(SUSPEND_NOTE, card));
+ col.markUndo(revertToProvidedState(R.string.menu_suspend_note, card));
// suspend note
col.getSched().suspendCards(cids);
}
@@ -597,7 +592,7 @@ protected static class UndoSuspendCardMulti extends Undoable {
* otherwise the action was "Unsuspend") */
public UndoSuspendCardMulti(Card[] cards, boolean[] originalSuspended,
boolean hasUnsuspended) {
- super((hasUnsuspended) ? Collection.DismissType.SUSPEND_CARD_MULTI: Collection.DismissType.UNSUSPEND_CARD_MULTI);
+ super((hasUnsuspended) ? R.string.menu_suspend_card : R.string.card_browser_unsuspend_card);
this.cards = cards;
this.originalSuspended = originalSuspended;
}
@@ -642,7 +637,7 @@ private static class UndoDeleteNoteMulti extends Undoable {
public UndoDeleteNoteMulti(Note[] notesArr, List allCards) {
- super(Collection.DismissType.DELETE_NOTE_MULTI);
+ super(R.string.card_browser_delete_card);
this.notesArr = notesArr;
this.allCards = allCards;
}
@@ -673,7 +668,7 @@ private static class UndoChangeDeckMulti extends Undoable {
public UndoChangeDeckMulti(Card[] cards, long[] originalDids) {
- super(Collection.DismissType.CHANGE_DECK_MULTI);
+ super(R.string.undo_action_change_deck_multi);
this.cards = cards;
this.originalDids = originalDids;
}
@@ -702,7 +697,7 @@ private static class UndoMarkNoteMulti extends Undoable {
/** @param hasUnmarked whether there were any unmarked card (in which card the action was "mark",
* otherwise the action was "Unmark") */
public UndoMarkNoteMulti(List originalMarked, List originalUnmarked, boolean hasUnmarked) {
- super((hasUnmarked) ? Collection.DismissType.MARK_NOTE_MULTI : Collection.DismissType.UNMARK_NOTE_MULTI);
+ super((hasUnmarked) ? R.string.card_browser_mark_card : R.string.card_browser_unmark_card);
this.originalMarked = originalMarked;
this.originalUnmarked = originalUnmarked;
}
@@ -721,14 +716,14 @@ private static class UndoRepositionRescheduleResetCards extends Undoable {
private final Card[] cards_copied;
- public UndoRepositionRescheduleResetCards(Collection.DismissType type, Card[] cards_copied) {
- super(type);
+ public UndoRepositionRescheduleResetCards(@StringRes int undoNameId, Card[] cards_copied) {
+ super(undoNameId);
this.cards_copied = cards_copied;
}
public @Nullable Card undo(@NonNull Collection col) {
- Timber.i("Undoing action of type %s on %d cards", getDismissType(), cards_copied.length);
+ Timber.i("Undoing action of type %s on %d cards", getClass(), cards_copied.length);
for (Card card : cards_copied) {
card.flush(false);
}
@@ -968,22 +963,22 @@ protected PairWithBoolean actualTask(Collection col, ProgressSenderAndCa
}
private abstract static class RescheduleRepositionReset extends DismissNotes {
- private final Collection.DismissType mType;
- public RescheduleRepositionReset(List cardIds, Collection.DismissType type) {
+ @StringRes private final int mUndoNameId;
+ public RescheduleRepositionReset(List cardIds, @StringRes int undoNameId) {
super(cardIds);
- mType = type;
+ mUndoNameId = undoNameId;
}
protected PairWithBoolean actualTask(Collection col, ProgressSenderAndCancelListener collectionTask, Card[] cards) {
AbstractSched sched = col.getSched();
// collect undo information, sensitive to memory pressure, same for all 3 cases
try {
- Timber.d("Saving undo information of type %s on %d cards", mType, cards.length);
+ Timber.d("Saving undo information of type %s on %d cards", getClass(), cards.length);
Card[] cards_copied = deepCopyCardArray(cards, collectionTask);
- Undoable repositionRescheduleResetCards = new UndoRepositionRescheduleResetCards(mType, cards_copied);
+ Undoable repositionRescheduleResetCards = new UndoRepositionRescheduleResetCards(mUndoNameId, cards_copied);
col.markUndo(repositionRescheduleResetCards);
} catch (CancellationException ce) {
- Timber.i(ce, "Cancelled while handling type %s, skipping undo", mType);
+ Timber.i(ce, "Cancelled while handling type %s, skipping undo", mUndoNameId);
}
actualActualTask(sched);
// In all cases schedule a new card so Reviewer doesn't sit on the old one
@@ -998,7 +993,7 @@ protected PairWithBoolean actualTask(Collection col, ProgressSenderAndCa
public static class RescheduleCards extends RescheduleRepositionReset {
private final int mSchedule;
public RescheduleCards(List cardIds, int schedule) {
- super(cardIds, RESCHEDULE_CARDS);
+ super(cardIds, R.string.card_editor_reschedule_card);
this.mSchedule = schedule;
}
@@ -1011,7 +1006,7 @@ protected void actualActualTask(AbstractSched sched) {
public static class RepositionCards extends RescheduleRepositionReset {
private final int mPosition;
public RepositionCards(List cardIds, int position) {
- super(cardIds, REPOSITION_CARDS);
+ super(cardIds, R.string.card_editor_reposition_card);
this.mPosition = position;
}
@@ -1023,7 +1018,7 @@ protected void actualActualTask(AbstractSched sched) {
public static class ResetCards extends RescheduleRepositionReset {
public ResetCards(List cardIds) {
- super(cardIds, RESET_CARDS);
+ super(cardIds, R.string.card_editor_reset_card);
}
@Override
diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
index 636e7688bd62..5506407e2dcc 100644
--- a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
+++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.java
@@ -87,7 +87,6 @@
import timber.log.Timber;
import static com.ichi2.async.CancelListener.isCancelled;
-import static com.ichi2.libanki.Collection.DismissType.REVIEW;
import static com.ichi2.libanki.Consts.DECK_DYN;
// Anki maintains a cache of used tags so it can quickly present a list of tags
@@ -155,39 +154,6 @@ public class Collection {
"'curModel': null, " + "'nextPos': 1, " + "'sortType': \"noteFld\", "
+ "'sortBackwards': False, 'addToCur': True }"; // add new to currently selected deck?
- public enum DismissType {
- REVIEW(R.string.undo_action_review),
- BURY_CARD(R.string.menu_bury_card),
- BURY_NOTE(R.string.menu_bury_note),
- SUSPEND_CARD(R.string.menu_suspend_card),
- SUSPEND_CARD_MULTI(R.string.menu_suspend_card),
- UNSUSPEND_CARD_MULTI(R.string.card_browser_unsuspend_card),
- SUSPEND_NOTE(R.string.menu_suspend_note),
- DELETE_NOTE(R.string.menu_delete_note),
- DELETE_NOTE_MULTI(R.string.card_browser_delete_card),
- CHANGE_DECK_MULTI(R.string.undo_action_change_deck_multi),
- MARK_NOTE_MULTI(R.string.card_browser_mark_card),
- UNMARK_NOTE_MULTI(R.string.card_browser_unmark_card),
- FLAG(R.string.menu_flag),
- REPOSITION_CARDS(R.string.card_editor_reposition_card),
- RESCHEDULE_CARDS(R.string.card_editor_reschedule_card),
- RESET_CARDS(R.string.card_editor_reset_card);
-
- @StringRes
- private final int mUndoNameId;
-
- DismissType(int undoNameId) {
- this.mUndoNameId = undoNameId;
- }
-
- private Locale getLocale(Resources resources) {
- return LanguageUtil.getLocaleCompat(resources);
- }
- public String getString(Resources res) {
- return res.getString(mUndoNameId).toLowerCase(getLocale(res));
- }
- }
-
private static final int UNDO_SIZE_MAX = 20;
@VisibleForTesting
@@ -1350,16 +1316,16 @@ public void clearUndo() {
/** Undo menu item name, or "" if undo unavailable. */
@VisibleForTesting
- public @Nullable DismissType undoType() {
+ public @Nullable Undoable undoType() {
if (mUndo.size() > 0) {
- return mUndo.getLast().getDismissType();
+ return mUndo.getLast();
}
return null;
}
public String undoName(Resources res) {
- DismissType type = undoType();
+ Undoable type = undoType();
if (type != null) {
- return type.getString(res);
+ return type.name(res);
}
return "";
}
@@ -1371,23 +1337,24 @@ public boolean undoAvailable() {
public @Nullable Card undo() {
Undoable lastUndo = mUndo.removeLast();
- Timber.d("undo() of type %s", lastUndo.getDismissType());
+ Timber.d("undo() of type %s", lastUndo.getClass());
return lastUndo.undo(this);
}
public void markUndo(@NonNull Undoable undo) {
- Timber.d("markUndo() of type %s", undo.getDismissType());
+ Timber.d("markUndo() of type %s", undo.getClass());
mUndo.add(undo);
while (mUndo.size() > UNDO_SIZE_MAX) {
mUndo.removeFirst();
}
}
- private static class UndoReview extends Undoable {
+ @VisibleForTesting
+ public static class UndoReview extends Undoable {
private final boolean mWasLeech;
@NonNull private final Card mClonedCard;
public UndoReview(boolean wasLeech, @NonNull Card clonedCard) {
- super(REVIEW);
+ super(R.string.undo_action_review);
mClonedCard = clonedCard;
mWasLeech = wasLeech;
}
diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Undoable.java b/AnkiDroid/src/main/java/com/ichi2/libanki/Undoable.java
index 1bd4f122921c..81e1d077cf7c 100644
--- a/AnkiDroid/src/main/java/com/ichi2/libanki/Undoable.java
+++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Undoable.java
@@ -2,30 +2,31 @@
import android.content.res.Resources;
-import com.ichi2.libanki.Collection.DismissType;
+import com.ichi2.utils.LanguageUtil;
import java.util.List;
+import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import timber.log.Timber;
public abstract class Undoable {
- private final DismissType mDt;
+ @StringRes public final int mUndoNameId;
/**
* For all descendants, we assume that a card/note/object passed as argument is never going to be changed again.
* It's the caller reponsability to clone the object if necessary.*/
- public Undoable(DismissType dt) {
- mDt = dt;
+ public Undoable(@StringRes int undoNameId) {
+ mUndoNameId = undoNameId;
}
- public String name(Resources res) {
- return mDt.getString(res);
+ private Locale getLocale(Resources resources) {
+ return LanguageUtil.getLocaleCompat(resources);
}
-
- public DismissType getDismissType() {
- return mDt;
+ public String name(Resources res) {
+ return res.getString(mUndoNameId).toLowerCase(getLocale(res));
}
/**
@@ -34,13 +35,13 @@ public DismissType getDismissType() {
* Returned positive integers are card id. Those ids is the card that was discarded and that may be sent back to the reviewer.*/
public abstract @Nullable Card undo(@NonNull Collection col);
- public static @NonNull Undoable revertToProvidedState (DismissType dt, Card card){
+ public static @NonNull Undoable revertToProvidedState (@StringRes int undoNameId, Card card){
Note note = card.note();
List cards = note.cards();
- return new Undoable(dt) {
+ return new Undoable(undoNameId) {
public @Nullable
Card undo(@NonNull Collection col) {
- Timber.i("Undo: %s", dt);
+ Timber.i("Undo: %d", undoNameId);
for (Card cc : cards) {
cc.flush(false);
}
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/UndoTest.java b/AnkiDroid/src/test/java/com/ichi2/libanki/UndoTest.java
index 9dca433706c3..e11a30b0033b 100644
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/UndoTest.java
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/UndoTest.java
@@ -12,6 +12,8 @@
import static com.ichi2.libanki.Consts.COUNT_REMAINING;
import static com.ichi2.libanki.Consts.QUEUE_TYPE_LRN;
import static com.ichi2.libanki.Consts.QUEUE_TYPE_NEW;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -120,7 +122,7 @@ public void test_review() throws Exception {
// performing a normal op will clear the review queue
c = col.getSched().getCard();
col.getSched().answerCard(c, 3);
- assertEquals(Collection.DismissType.REVIEW, col.undoType());
+ assertThat(col.undoType(), is(instanceOf(Collection.UndoReview.class)));
col.save("foo");
// Upstream, "save" can be undone. This test fails here because it's not the case in AnkiDroid
assumeThat(col.undoName(getTargetContext().getResources()), is("foo"));
From 20fe1e54d046d9e524ac2feb1a825191e6a11123 Mon Sep 17 00:00:00 2001
From: David Allison <62114487+david-allison-1@users.noreply.github.com>
Date: Fri, 2 Apr 2021 17:19:27 +0100
Subject: [PATCH 032/171] Ignore Invalid field naming for wildplot
It's not worth worrying about the m variable prefix as we're not
going to edit external library code
Issue 8387
---
.../main/java/com/wildplot/android/parsing/Atom.java | 11 ++++++++++-
.../android/parsing/AtomTypes/FunctionXAtom.java | 9 ++++++++-
.../android/parsing/AtomTypes/FunctionXYAtom.java | 9 ++++++++-
.../android/parsing/AtomTypes/MathFunctionAtom.java | 3 +++
.../android/parsing/AtomTypes/NumberAtom.java | 3 +++
.../android/parsing/AtomTypes/VariableAtom.java | 3 +++
.../android/parsing/AtomTypes/XVariableAtom.java | 3 +++
.../android/parsing/AtomTypes/YVariableAtom.java | 3 +++
.../java/com/wildplot/android/parsing/Expression.java | 3 +++
.../java/com/wildplot/android/parsing/Factor.java | 3 +++
.../main/java/com/wildplot/android/parsing/Pow.java | 3 +++
.../main/java/com/wildplot/android/parsing/Term.java | 3 +++
.../com/wildplot/android/parsing/TopLevelParser.java | 3 +++
.../java/com/wildplot/android/rendering/BarGraph.java | 3 +++
.../wildplot/android/rendering/DrawableContainer.java | 3 +++
.../wildplot/android/rendering/LegendDrawable.java | 3 +++
.../java/com/wildplot/android/rendering/Lines.java | 9 ++++++++-
.../wildplot/android/rendering/MultiScreenPart.java | 3 +++
.../java/com/wildplot/android/rendering/PieChart.java | 3 +++
.../com/wildplot/android/rendering/PlotSheet.java | 7 ++++++-
.../java/com/wildplot/android/rendering/XAxis.java | 3 +++
.../java/com/wildplot/android/rendering/XGrid.java | 3 +++
.../java/com/wildplot/android/rendering/YAxis.java | 3 +++
.../java/com/wildplot/android/rendering/YGrid.java | 3 +++
.../android/rendering/graphics/wrapper/ColorWrap.java | 3 +++
.../rendering/graphics/wrapper/FontMetricsWrap.java | 3 +++
.../rendering/graphics/wrapper/GraphicsWrap.java | 2 ++
.../rendering/graphics/wrapper/StrokeWrap.java | 3 +++
28 files changed, 108 insertions(+), 5 deletions(-)
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/Atom.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/Atom.java
index e2801799e591..6bcf1299901e 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/Atom.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/Atom.java
@@ -15,8 +15,17 @@
****************************************************************************************/
package com.wildplot.android.parsing;
-import com.wildplot.android.parsing.AtomTypes.*;
+import android.annotation.SuppressLint;
+import com.wildplot.android.parsing.AtomTypes.FunctionXAtom;
+import com.wildplot.android.parsing.AtomTypes.FunctionXYAtom;
+import com.wildplot.android.parsing.AtomTypes.MathFunctionAtom;
+import com.wildplot.android.parsing.AtomTypes.NumberAtom;
+import com.wildplot.android.parsing.AtomTypes.VariableAtom;
+import com.wildplot.android.parsing.AtomTypes.XVariableAtom;
+import com.wildplot.android.parsing.AtomTypes.YVariableAtom;
+
+@SuppressLint("FieldNamingPatternDetector")
public class Atom implements TreeElement {
private final TopLevelParser parser;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/FunctionXAtom.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/FunctionXAtom.java
index 9a77735d0ca8..c82db7bc2a8d 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/FunctionXAtom.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/FunctionXAtom.java
@@ -15,10 +15,17 @@
****************************************************************************************/
package com.wildplot.android.parsing.AtomTypes;
-import com.wildplot.android.parsing.*;
+import android.annotation.SuppressLint;
+
+import com.wildplot.android.parsing.Atom;
+import com.wildplot.android.parsing.Expression;
+import com.wildplot.android.parsing.ExpressionFormatException;
+import com.wildplot.android.parsing.TopLevelParser;
+import com.wildplot.android.parsing.TreeElement;
import java.util.regex.Pattern;
+@SuppressLint("FieldNamingPatternDetector")
public class FunctionXAtom implements TreeElement {
private Atom.AtomType atomType = Atom.AtomType.FUNCTION_X;
private final TopLevelParser parser;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/FunctionXYAtom.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/FunctionXYAtom.java
index 6d082a835762..16ee3636f90a 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/FunctionXYAtom.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/FunctionXYAtom.java
@@ -16,13 +16,20 @@
package com.wildplot.android.parsing.AtomTypes;
-import com.wildplot.android.parsing.*;
+import android.annotation.SuppressLint;
+
+import com.wildplot.android.parsing.Atom;
+import com.wildplot.android.parsing.Expression;
+import com.wildplot.android.parsing.ExpressionFormatException;
+import com.wildplot.android.parsing.TopLevelParser;
+import com.wildplot.android.parsing.TreeElement;
import java.util.regex.Pattern;
/**
* @author Michael Goldbach
*/
+@SuppressLint("FieldNamingPatternDetector")
public class FunctionXYAtom implements TreeElement {
private Atom.AtomType atomType = Atom.AtomType.FUNCTION_X;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/MathFunctionAtom.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/MathFunctionAtom.java
index 95140774c9c9..07fa64205baf 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/MathFunctionAtom.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/MathFunctionAtom.java
@@ -15,12 +15,15 @@
****************************************************************************************/
package com.wildplot.android.parsing.AtomTypes;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.parsing.Expression;
import com.wildplot.android.parsing.ExpressionFormatException;
import com.wildplot.android.parsing.TopLevelParser;
import com.wildplot.android.parsing.TreeElement;
+@SuppressLint("FieldNamingPatternDetector")
public class MathFunctionAtom implements TreeElement {
private final TopLevelParser parser;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/NumberAtom.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/NumberAtom.java
index 84a187e98e00..fff9338c38ff 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/NumberAtom.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/NumberAtom.java
@@ -15,12 +15,15 @@
****************************************************************************************/
package com.wildplot.android.parsing.AtomTypes;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.parsing.Atom;
import com.wildplot.android.parsing.ExpressionFormatException;
import com.wildplot.android.parsing.TreeElement;
import timber.log.Timber;
+@SuppressLint("FieldNamingPatternDetector")
public class NumberAtom implements TreeElement {
private Atom.AtomType atomType = Atom.AtomType.NUMBER;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/VariableAtom.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/VariableAtom.java
index da5a2e24ba48..ee3c84262ced 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/VariableAtom.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/VariableAtom.java
@@ -15,6 +15,8 @@
****************************************************************************************/
package com.wildplot.android.parsing.AtomTypes;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.parsing.Atom;
import com.wildplot.android.parsing.ExpressionFormatException;
import com.wildplot.android.parsing.TopLevelParser;
@@ -23,6 +25,7 @@
import java.util.regex.Pattern;
+@SuppressLint("FieldNamingPatternDetector")
public class VariableAtom implements TreeElement {
private Atom.AtomType atomType = Atom.AtomType.NUMBER;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/XVariableAtom.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/XVariableAtom.java
index 94dfdd8c7994..a584ed1d2525 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/XVariableAtom.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/XVariableAtom.java
@@ -15,11 +15,14 @@
****************************************************************************************/
package com.wildplot.android.parsing.AtomTypes;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.parsing.Atom;
import com.wildplot.android.parsing.ExpressionFormatException;
import com.wildplot.android.parsing.TopLevelParser;
import com.wildplot.android.parsing.TreeElement;
+@SuppressLint("FieldNamingPatternDetector")
public class XVariableAtom implements TreeElement {
private final Atom.AtomType atomType = Atom.AtomType.VARIABLE;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/YVariableAtom.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/YVariableAtom.java
index 53e66dfeb80c..d89f85486255 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/YVariableAtom.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/AtomTypes/YVariableAtom.java
@@ -15,11 +15,14 @@
****************************************************************************************/
package com.wildplot.android.parsing.AtomTypes;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.parsing.Atom;
import com.wildplot.android.parsing.ExpressionFormatException;
import com.wildplot.android.parsing.TopLevelParser;
import com.wildplot.android.parsing.TreeElement;
+@SuppressLint("FieldNamingPatternDetector")
public class YVariableAtom implements TreeElement {
private final Atom.AtomType atomType = Atom.AtomType.VARIABLE;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/Expression.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/Expression.java
index 2bf47daa4759..e4b0ff8e7388 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/Expression.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/Expression.java
@@ -16,6 +16,9 @@
package com.wildplot.android.parsing;
+import android.annotation.SuppressLint;
+
+@SuppressLint("FieldNamingPatternDetector")
public class Expression implements TreeElement {
private final TopLevelParser parser;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/Factor.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/Factor.java
index a4affe98a166..20972d5abdc6 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/Factor.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/Factor.java
@@ -16,6 +16,9 @@
package com.wildplot.android.parsing;
+import android.annotation.SuppressLint;
+
+@SuppressLint("FieldNamingPatternDetector")
public class Factor implements TreeElement {
private final TopLevelParser parser;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/Pow.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/Pow.java
index 85430b5ec4d1..2e361a78d987 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/Pow.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/Pow.java
@@ -16,6 +16,9 @@
package com.wildplot.android.parsing;
+import android.annotation.SuppressLint;
+
+@SuppressLint("FieldNamingPatternDetector")
public class Pow implements TreeElement {
private final TopLevelParser parser;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/Term.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/Term.java
index de764b78d41d..75fd3a97539e 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/Term.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/Term.java
@@ -16,6 +16,9 @@
package com.wildplot.android.parsing;
+import android.annotation.SuppressLint;
+
+@SuppressLint("FieldNamingPatternDetector")
public class Term implements TreeElement {
private final TopLevelParser parser;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/parsing/TopLevelParser.java b/AnkiDroid/src/main/java/com/wildplot/android/parsing/TopLevelParser.java
index 8b6aafba2b06..59f7635f38ba 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/parsing/TopLevelParser.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/parsing/TopLevelParser.java
@@ -15,12 +15,15 @@
****************************************************************************************/
package com.wildplot.android.parsing;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.rendering.interfaces.Function2D;
import com.wildplot.android.rendering.interfaces.Function3D;
import java.util.HashMap;
import java.util.regex.Pattern;
+@SuppressLint("FieldNamingPatternDetector")
public class TopLevelParser implements Function2D, Function3D, Cloneable {
private final HashMap parserRegister;
private final HashMap varMap = new HashMap<>(2); // Number form initVarMap
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/BarGraph.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/BarGraph.java
index 47997464ef05..f9976abb9a4f 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/BarGraph.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/BarGraph.java
@@ -15,6 +15,8 @@
****************************************************************************************/
package com.wildplot.android.rendering;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.rendering.graphics.wrapper.ColorWrap;
import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap;
import com.wildplot.android.rendering.graphics.wrapper.RectangleWrap;
@@ -25,6 +27,7 @@
/**
* BarGraph uses a point matrix or a function to render bar graphs on PlotSheet object
*/
+@SuppressLint("FieldNamingPatternDetector")
public class BarGraph implements Drawable, Legendable {
private String mName = "";
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/DrawableContainer.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/DrawableContainer.java
index 0222579314b3..c0d1ef84f416 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/DrawableContainer.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/DrawableContainer.java
@@ -15,12 +15,15 @@
****************************************************************************************/
package com.wildplot.android.rendering;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap;
import com.wildplot.android.rendering.interfaces.Drawable;
import java.util.Vector;
+@SuppressLint("FieldNamingPatternDetector")
public class DrawableContainer implements Drawable {
private final Vector drawableVector = new Vector<>();
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/LegendDrawable.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/LegendDrawable.java
index b5de44fee045..29722af687f1 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/LegendDrawable.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/LegendDrawable.java
@@ -15,12 +15,15 @@
****************************************************************************************/
package com.wildplot.android.rendering;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.rendering.graphics.wrapper.ColorWrap;
import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap;
import com.wildplot.android.rendering.interfaces.Drawable;
import com.wildplot.android.rendering.interfaces.Legendable;
+@SuppressLint("FieldNamingPatternDetector")
public class LegendDrawable implements Drawable, Legendable {
private String mName = "";
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/Lines.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/Lines.java
index 16ceff63d81c..76f79ed41237 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/Lines.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/Lines.java
@@ -15,7 +15,13 @@
****************************************************************************************/
package com.wildplot.android.rendering;
-import com.wildplot.android.rendering.graphics.wrapper.*;
+import android.annotation.SuppressLint;
+
+import com.wildplot.android.rendering.graphics.wrapper.BasicStrokeWrap;
+import com.wildplot.android.rendering.graphics.wrapper.ColorWrap;
+import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap;
+import com.wildplot.android.rendering.graphics.wrapper.RectangleWrap;
+import com.wildplot.android.rendering.graphics.wrapper.StrokeWrap;
import com.wildplot.android.rendering.interfaces.Drawable;
import com.wildplot.android.rendering.interfaces.Legendable;
@@ -24,6 +30,7 @@
* The LinesPoints objects draw points from a data array and connect them with lines on.
* These LinesPoints are drawn onto a PlotSheet object
*/
+@SuppressLint("FieldNamingPatternDetector")
public class Lines implements Drawable, Legendable {
private boolean mHasShadow = false;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/MultiScreenPart.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/MultiScreenPart.java
index b43f3cb5bff4..6d0e45be5b61 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/MultiScreenPart.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/MultiScreenPart.java
@@ -15,6 +15,8 @@
****************************************************************************************/
package com.wildplot.android.rendering;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.rendering.interfaces.Drawable;
import java.util.Vector;
@@ -23,6 +25,7 @@
* This class is used to store informations for a certain plot in a multi-plot sheet.
* The informations are the drawables for this plotsheet and the x and y limitations
*/
+@SuppressLint("FieldNamingPatternDetector")
public class MultiScreenPart {
private final double[] xRange;
private final double[] yRange;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/PieChart.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/PieChart.java
index f3f13fe82836..2625d8189588 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/PieChart.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/PieChart.java
@@ -15,6 +15,8 @@
****************************************************************************************/
package com.wildplot.android.rendering;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.rendering.graphics.wrapper.ColorWrap;
import com.wildplot.android.rendering.graphics.wrapper.FontMetricsWrap;
import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap;
@@ -24,6 +26,7 @@
import androidx.annotation.NonNull;
+@SuppressLint("FieldNamingPatternDetector")
public class PieChart implements Drawable, Legendable {
// First sector starts at 12 o'clock.
private static final float FIRST_SECTOR_OFFSET = -90;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/PlotSheet.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/PlotSheet.java
index 5216095e7c25..8c4787270ada 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/PlotSheet.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/PlotSheet.java
@@ -16,9 +16,13 @@
package com.wildplot.android.rendering;
+import android.annotation.SuppressLint;
import android.graphics.Typeface;
-import com.wildplot.android.rendering.graphics.wrapper.*;
+import com.wildplot.android.rendering.graphics.wrapper.ColorWrap;
+import com.wildplot.android.rendering.graphics.wrapper.FontMetricsWrap;
+import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap;
+import com.wildplot.android.rendering.graphics.wrapper.RectangleWrap;
import com.wildplot.android.rendering.interfaces.Drawable;
import com.wildplot.android.rendering.interfaces.Legendable;
@@ -35,6 +39,7 @@
* This is a sheet that is used to plot mathematical functions including coordinate systems and optional extras like
* legends and descriptors. Additionally all conversions from image to plot coordinates are done here
*/
+@SuppressLint("FieldNamingPatternDetector")
public class PlotSheet implements Drawable {
protected Typeface typeface = Typeface.DEFAULT;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/XAxis.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/XAxis.java
index 395f40e8d141..f48837182da7 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/XAxis.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/XAxis.java
@@ -16,6 +16,8 @@
package com.wildplot.android.rendering;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.rendering.graphics.wrapper.FontMetricsWrap;
import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap;
import com.wildplot.android.rendering.graphics.wrapper.RectangleWrap;
@@ -26,6 +28,7 @@
/**
* This Class represents a Drawable x-axis
*/
+@SuppressLint("FieldNamingPatternDetector")
public class XAxis implements Drawable {
private boolean isIntegerNumbering = false;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/XGrid.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/XGrid.java
index 9b04ac1abc41..a2119ba311f1 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/XGrid.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/XGrid.java
@@ -15,6 +15,8 @@
****************************************************************************************/
package com.wildplot.android.rendering;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.rendering.graphics.wrapper.ColorWrap;
import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap;
import com.wildplot.android.rendering.graphics.wrapper.RectangleWrap;
@@ -24,6 +26,7 @@
/**
* This class represents grid lines parallel to the x-axis
*/
+@SuppressLint("FieldNamingPatternDetector")
public class XGrid implements Drawable {
/**
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/YAxis.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/YAxis.java
index cbb9e6f10c00..af0368054f61 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/YAxis.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/YAxis.java
@@ -15,6 +15,8 @@
****************************************************************************************/
package com.wildplot.android.rendering;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.rendering.graphics.wrapper.FontMetricsWrap;
import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap;
import com.wildplot.android.rendering.graphics.wrapper.RectangleWrap;
@@ -25,6 +27,7 @@
/**
* This Class represents a Drawable x-axis
*/
+@SuppressLint("FieldNamingPatternDetector")
public class YAxis implements Drawable {
private boolean mHasNumbersRotated = false;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/YGrid.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/YGrid.java
index 1e7fe8eae5d5..655c2ea44fa7 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/YGrid.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/YGrid.java
@@ -15,6 +15,8 @@
****************************************************************************************/
package com.wildplot.android.rendering;
+import android.annotation.SuppressLint;
+
import com.wildplot.android.rendering.graphics.wrapper.ColorWrap;
import com.wildplot.android.rendering.graphics.wrapper.GraphicsWrap;
import com.wildplot.android.rendering.graphics.wrapper.RectangleWrap;
@@ -24,6 +26,7 @@
/**
* This class represents grid lines parallel to the y-axis
*/
+@SuppressLint("FieldNamingPatternDetector")
public class YGrid implements Drawable {
/**
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/ColorWrap.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/ColorWrap.java
index c7bdd02797e9..f47636e6d568 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/ColorWrap.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/ColorWrap.java
@@ -15,6 +15,9 @@
****************************************************************************************/
package com.wildplot.android.rendering.graphics.wrapper;
+import android.annotation.SuppressLint;
+
+@SuppressLint("FieldNamingPatternDetector")
public class ColorWrap {
//android.graphics.Color
private final int colorValue;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/FontMetricsWrap.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/FontMetricsWrap.java
index 48d5ff9e540e..e1abdce56c4f 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/FontMetricsWrap.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/FontMetricsWrap.java
@@ -15,6 +15,9 @@
****************************************************************************************/
package com.wildplot.android.rendering.graphics.wrapper;
+import android.annotation.SuppressLint;
+
+@SuppressLint("FieldNamingPatternDetector")
public class FontMetricsWrap {
private final GraphicsWrap g;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/GraphicsWrap.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/GraphicsWrap.java
index 82daba53df86..b1421e723c7c 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/GraphicsWrap.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/GraphicsWrap.java
@@ -15,6 +15,7 @@
****************************************************************************************/
package com.wildplot.android.rendering.graphics.wrapper;
+import android.annotation.SuppressLint;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
@@ -26,6 +27,7 @@
*
* @author Michael Goldbach
*/
+@SuppressLint("FieldNamingPatternDetector")
public class GraphicsWrap {
private final Canvas canvas;
private final Paint paint;
diff --git a/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/StrokeWrap.java b/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/StrokeWrap.java
index 86781c77a8f1..ba18d44e1d77 100644
--- a/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/StrokeWrap.java
+++ b/AnkiDroid/src/main/java/com/wildplot/android/rendering/graphics/wrapper/StrokeWrap.java
@@ -15,6 +15,9 @@
****************************************************************************************/
package com.wildplot.android.rendering.graphics.wrapper;
+import android.annotation.SuppressLint;
+
+@SuppressLint("FieldNamingPatternDetector")
public class StrokeWrap {
private final float strokeSize;
From a2eb2093ab29943b07b3452ab96eedc018234298 Mon Sep 17 00:00:00 2001
From: Arthur Milchior
Date: Wed, 31 Mar 2021 07:00:54 +0200
Subject: [PATCH 033/171] NF: Introduce List assertion
This way, it uses same tools as array equality and will show descriptive message about the difference
---
.../java/com/ichi2/libanki/ModelTest.java | 109 +++++++++---------
.../java/com/ichi2/testutils/AnkiAssert.java | 3 +-
.../test/java/com/ichi2/utils/ListUtil.java | 80 +++++++++++++
3 files changed, 137 insertions(+), 55 deletions(-)
create mode 100644 AnkiDroid/src/test/java/com/ichi2/utils/ListUtil.java
diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/ModelTest.java b/AnkiDroid/src/test/java/com/ichi2/libanki/ModelTest.java
index 6441f977d37a..bf7b2588856b 100644
--- a/AnkiDroid/src/test/java/com/ichi2/libanki/ModelTest.java
+++ b/AnkiDroid/src/test/java/com/ichi2/libanki/ModelTest.java
@@ -25,6 +25,7 @@
import static com.ichi2.libanki.Models.REQ_ALL;
import static com.ichi2.libanki.Models.REQ_ANY;
import static com.ichi2.libanki.Utils.stripHTML;
+import static com.ichi2.utils.ListUtil.assertListEquals;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
@@ -199,7 +200,7 @@ public void test_cloze_empty() {
Models mm = col.getModels();
Model cloze_model = mm.byName("Cloze");
mm.setCurrent(cloze_model);
- assertEquals(new ArrayList<>(Arrays.asList(0, 1)), Models.availOrds(cloze_model, new String[]{"{{c1::Empty}} and {{c2::}}", ""}));
+ assertListEquals(Arrays.asList(0, 1), Models.availOrds(cloze_model, new String[]{"{{c1::Empty}} and {{c2::}}", ""}));
}
@@ -482,10 +483,10 @@ public void regression_test_pipe() {
@Test
public void test_getNamesOfFieldContainingCloze() {
- assertEquals(new ArrayList<>(), Models.getNamesOfFieldsContainingCloze(""));
+ assertListEquals(new ArrayList<>(), Models.getNamesOfFieldsContainingCloze(""));
String example = "{{cloze::foo}} <%cloze:bar%>";
- assertEquals(Arrays.asList("foo", "bar"), Models.getNamesOfFieldsContainingCloze(example));
- assertEquals(Arrays.asList("foo", "bar"), Models.getNamesOfFieldsContainingCloze(example));
+ assertListEquals(Arrays.asList("foo", "bar"), Models.getNamesOfFieldsContainingCloze(example));
+ assertListEquals(Arrays.asList("foo", "bar"), Models.getNamesOfFieldsContainingCloze(example));
}
@Test
@@ -509,32 +510,32 @@ public void avail_standard_order_test() {
Model basic = mm.byName("Basic");
Model reverse = mm.byName("Basic (and reversed card)");
- assertEquals(new ArrayList<>(), Models._availStandardOrds(basic, new String[]{"", ""}));
- assertEquals(new ArrayList<>(), Models._availStandardOrds(basic, new String[]{"", "Back"}));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models._availStandardOrds(basic, new String[]{"Foo", ""}));
- assertEquals(new ArrayList<>(Arrays.asList()), Models._availStandardOrds(basic, new String[]{" \t ", ""}));
- assertEquals(new ArrayList<>(), Models._availStandardOrds(reverse, new String[]{"", ""}));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models._availStandardOrds(reverse, new String[]{"Foo", ""}));
- assertEquals(new ArrayList<>(Arrays.asList(0, 1)), Models._availStandardOrds(reverse, new String[]{"Foo", "Bar"}));
- assertEquals(new ArrayList<>(Arrays.asList(1)), Models._availStandardOrds(reverse, new String[]{" \t ", "Bar"}));
-
- assertEquals(new ArrayList<>(), Models._availStandardOrds(basic, new String[]{"", ""}, false) );
- assertEquals(new ArrayList<>(), Models._availStandardOrds(basic, new String[]{"", "Back"}, false));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models._availStandardOrds(basic, new String[]{"Foo", ""}, false));
- assertEquals(new ArrayList<>(Arrays.asList()), Models._availStandardOrds(basic, new String[]{" \t ", ""}, false));
- assertEquals(new ArrayList<>(), Models._availStandardOrds(reverse, new String[]{"", ""}, false));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models._availStandardOrds(reverse, new String[]{"Foo", ""}, false));
- assertEquals(new ArrayList<>(Arrays.asList(0, 1)), Models._availStandardOrds(reverse, new String[]{"Foo", "Bar"}, false));
- assertEquals(new ArrayList<>(Arrays.asList(1)), Models._availStandardOrds(reverse, new String[]{" \t ", "Bar"}, false));
-
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models._availStandardOrds(basic, new String[]{"", ""}, true) );
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models._availStandardOrds(basic, new String[]{"", "Back"}, true));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models._availStandardOrds(basic, new String[]{"Foo", ""}, true));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models._availStandardOrds(basic, new String[]{" \t ", ""}, true));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models._availStandardOrds(reverse, new String[]{"", ""}, true));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models._availStandardOrds(reverse, new String[]{"Foo", ""}, true));
- assertEquals(new ArrayList<>(Arrays.asList(0, 1)), Models._availStandardOrds(reverse, new String[]{"Foo", "Bar"}, true));
- assertEquals(new ArrayList<>(Arrays.asList(1)), Models._availStandardOrds(reverse, new String[]{" \t ", "Bar"}, true));
+ assertListEquals(new ArrayList<>(), Models._availStandardOrds(basic, new String[]{"", ""}));
+ assertListEquals(new ArrayList<>(), Models._availStandardOrds(basic, new String[]{"", "Back"}));
+ assertListEquals(Arrays.asList(0), Models._availStandardOrds(basic, new String[]{"Foo", ""}));
+ assertListEquals(Arrays.asList(), Models._availStandardOrds(basic, new String[]{" \t ", ""}));
+ assertListEquals(new ArrayList<>(), Models._availStandardOrds(reverse, new String[]{"", ""}));
+ assertListEquals(Arrays.asList(0), Models._availStandardOrds(reverse, new String[]{"Foo", ""}));
+ assertListEquals(Arrays.asList(0, 1), Models._availStandardOrds(reverse, new String[]{"Foo", "Bar"}));
+ assertListEquals(Arrays.asList(1), Models._availStandardOrds(reverse, new String[]{" \t ", "Bar"}));
+
+ assertListEquals(new ArrayList<>(), Models._availStandardOrds(basic, new String[]{"", ""}, false) );
+ assertListEquals(new ArrayList<>(), Models._availStandardOrds(basic, new String[]{"", "Back"}, false));
+ assertListEquals(Arrays.asList(0), Models._availStandardOrds(basic, new String[]{"Foo", ""}, false));
+ assertListEquals(Arrays.asList(), Models._availStandardOrds(basic, new String[]{" \t ", ""}, false));
+ assertListEquals(new ArrayList<>(), Models._availStandardOrds(reverse, new String[]{"", ""}, false));
+ assertListEquals(Arrays.asList(0), Models._availStandardOrds(reverse, new String[]{"Foo", ""}, false));
+ assertListEquals(Arrays.asList(0, 1), Models._availStandardOrds(reverse, new String[]{"Foo", "Bar"}, false));
+ assertListEquals(Arrays.asList(1), Models._availStandardOrds(reverse, new String[]{" \t ", "Bar"}, false));
+
+ assertListEquals(Arrays.asList(0), Models._availStandardOrds(basic, new String[]{"", ""}, true) );
+ assertListEquals(Arrays.asList(0), Models._availStandardOrds(basic, new String[]{"", "Back"}, true));
+ assertListEquals(Arrays.asList(0), Models._availStandardOrds(basic, new String[]{"Foo", ""}, true));
+ assertListEquals(Arrays.asList(0), Models._availStandardOrds(basic, new String[]{" \t ", ""}, true));
+ assertListEquals(Arrays.asList(0), Models._availStandardOrds(reverse, new String[]{"", ""}, true));
+ assertListEquals(Arrays.asList(0), Models._availStandardOrds(reverse, new String[]{"Foo", ""}, true));
+ assertListEquals(Arrays.asList(0, 1), Models._availStandardOrds(reverse, new String[]{"Foo", "Bar"}, true));
+ assertListEquals(Arrays.asList(1), Models._availStandardOrds(reverse, new String[]{" \t ", "Bar"}, true));
}
@Test
@@ -544,33 +545,33 @@ public void avail_ords_test() {
Model basic = mm.byName("Basic");
Model reverse = mm.byName("Basic (and reversed card)");
- assertEquals(new ArrayList<>(), Models.availOrds(basic, new String[]{"", ""}));
- assertEquals(new ArrayList<>(), Models.availOrds(basic, new String[]{"", "Back"}));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models.availOrds(basic, new String[]{"Foo", ""}));
- assertEquals(new ArrayList<>(Arrays.asList()), Models.availOrds(basic, new String[]{" \t ", ""}));
- assertEquals(new ArrayList<>(), Models.availOrds(reverse, new String[]{"", ""}));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models.availOrds(reverse, new String[]{"Foo", ""}));
- assertEquals(new ArrayList<>(Arrays.asList(0, 1)), Models.availOrds(reverse, new String[]{"Foo", "Bar"}));
- assertEquals(new ArrayList<>(Arrays.asList(1)), Models.availOrds(reverse, new String[]{" \t ", "Bar"}));
+ assertListEquals(new ArrayList<>(), Models.availOrds(basic, new String[]{"", ""}));
+ assertListEquals(new ArrayList<>(), Models.availOrds(basic, new String[]{"", "Back"}));
+ assertListEquals(Arrays.asList(0), Models.availOrds(basic, new String[]{"Foo", ""}));
+ assertListEquals(Arrays.asList(), Models.availOrds(basic, new String[]{" \t ", ""}));
+ assertListEquals(new ArrayList<>(), Models.availOrds(reverse, new String[]{"", ""}));
+ assertListEquals(Arrays.asList(0), Models.availOrds(reverse, new String[]{"Foo", ""}));
+ assertListEquals(Arrays.asList(0, 1), Models.availOrds(reverse, new String[]{"Foo", "Bar"}));
+ assertListEquals(Arrays.asList(1), Models.availOrds(reverse, new String[]{" \t ", "Bar"}));
for (Models.AllowEmpty allow : new Models.AllowEmpty[] {Models.AllowEmpty.ONLY_CLOZE, Models.AllowEmpty.FALSE}) {
- assertEquals(new ArrayList<>(), Models.availOrds(basic, new String[] {"", ""}, allow));
- assertEquals(new ArrayList<>(), Models.availOrds(basic, new String[] {"", "Back"}, allow));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models.availOrds(basic, new String[] {"Foo", ""}, allow));
- assertEquals(new ArrayList<>(Arrays.asList()), Models.availOrds(basic, new String[] {" \t ", ""}, allow));
- assertEquals(new ArrayList<>(), Models.availOrds(reverse, new String[] {"", ""}, allow));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models.availOrds(reverse, new String[] {"Foo", ""}, allow));
- assertEquals(new ArrayList<>(Arrays.asList(0, 1)), Models.availOrds(reverse, new String[] {"Foo", "Bar"}, allow));
- assertEquals(new ArrayList<>(Arrays.asList(1)), Models.availOrds(reverse, new String[] {" \t ", "Bar"}, allow));
+ assertListEquals(new ArrayList<>(), Models.availOrds(basic, new String[] {"", ""}, allow));
+ assertListEquals(new ArrayList<>(), Models.availOrds(basic, new String[] {"", "Back"}, allow));
+ assertListEquals(Arrays.asList(0), Models.availOrds(basic, new String[] {"Foo", ""}, allow));
+ assertListEquals(Arrays.asList(), Models.availOrds(basic, new String[] {" \t ", ""}, allow));
+ assertListEquals(new ArrayList<>(), Models.availOrds(reverse, new String[] {"", ""}, allow));
+ assertListEquals(Arrays.asList(0), Models.availOrds(reverse, new String[] {"Foo", ""}, allow));
+ assertListEquals(Arrays.asList(0, 1), Models.availOrds(reverse, new String[] {"Foo", "Bar"}, allow));
+ assertListEquals(Arrays.asList(1), Models.availOrds(reverse, new String[] {" \t ", "Bar"}, allow));
}
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models.availOrds(basic, new String[]{"", ""}, Models.AllowEmpty.TRUE));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models.availOrds(basic, new String[]{"", "Back"}, Models.AllowEmpty.TRUE));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models.availOrds(basic, new String[]{"Foo", ""}, Models.AllowEmpty.TRUE));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models.availOrds(basic, new String[]{" \t ", ""}, Models.AllowEmpty.TRUE));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models.availOrds(reverse, new String[]{"", ""}, Models.AllowEmpty.TRUE));
- assertEquals(new ArrayList<>(Arrays.asList(0)), Models.availOrds(reverse, new String[]{"Foo", ""}, Models.AllowEmpty.TRUE));
- assertEquals(new ArrayList<>(Arrays.asList(0, 1)), Models.availOrds(reverse, new String[]{"Foo", "Bar"}, Models.AllowEmpty.TRUE));
- assertEquals(new ArrayList<>(Arrays.asList(1)), Models.availOrds(reverse, new String[]{" \t ", "Bar"}, Models.AllowEmpty.TRUE));
+ assertListEquals(Arrays.asList(0), Models.availOrds(basic, new String[]{"", ""}, Models.AllowEmpty.TRUE));
+ assertListEquals(Arrays.asList(0), Models.availOrds(basic, new String[]{"", "Back"}, Models.AllowEmpty.TRUE));
+ assertListEquals(Arrays.asList(0), Models.availOrds(basic, new String[]{"Foo", ""}, Models.AllowEmpty.TRUE));
+ assertListEquals(Arrays.asList(0), Models.availOrds(basic, new String[]{" \t ", ""}, Models.AllowEmpty.TRUE));
+ assertListEquals(Arrays.asList(0), Models.availOrds(reverse, new String[]{"", ""}, Models.AllowEmpty.TRUE));
+ assertListEquals(Arrays.asList(0), Models.availOrds(reverse, new String[]{"Foo", ""}, Models.AllowEmpty.TRUE));
+ assertListEquals(Arrays.asList(0, 1), Models.availOrds(reverse, new String[]{"Foo", "Bar"}, Models.AllowEmpty.TRUE));
+ assertListEquals(Arrays.asList(1), Models.availOrds(reverse, new String[]{" \t ", "Bar"}, Models.AllowEmpty.TRUE));
}
}
diff --git a/AnkiDroid/src/test/java/com/ichi2/testutils/AnkiAssert.java b/AnkiDroid/src/test/java/com/ichi2/testutils/AnkiAssert.java
index 941ebd949533..fa8d1b16270e 100644
--- a/AnkiDroid/src/test/java/com/ichi2/testutils/AnkiAssert.java
+++ b/AnkiDroid/src/test/java/com/ichi2/testutils/AnkiAssert.java
@@ -10,6 +10,7 @@
import androidx.annotation.NonNull;
+import static com.ichi2.utils.ListUtil.assertListEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -47,7 +48,7 @@ public static T assertThrows(Runnable r, Class clazz) {
}
public static void assertEqualsArrayList(T[] expected, List actual) {
- assertEquals(Arrays.asList(expected), actual);
+ assertListEquals(Arrays.asList(expected), actual);
}
diff --git a/AnkiDroid/src/test/java/com/ichi2/utils/ListUtil.java b/AnkiDroid/src/test/java/com/ichi2/utils/ListUtil.java
new file mode 100644
index 000000000000..97efdaf36dcc
--- /dev/null
+++ b/AnkiDroid/src/test/java/com/ichi2/utils/ListUtil.java
@@ -0,0 +1,80 @@
+/****************************************************************************************
+ * Copyright (c) 2021 Arthur Milchior *
+ * *
+ * This program is free software; you can redistribute it and/or modify it under *
+ * the terms of the GNU General Public License as published by the Free Software *
+ * Foundation; either version 3 of the License, or (at your option) any later *
+ * version. *
+ * *
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY *
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A *
+ * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License along with *
+ * this program. If not, see . *
+ ****************************************************************************************/
+package com.ichi2.utils;
+
+import org.junit.Test;
+import org.junit.internal.ArrayComparisonFailure;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertThrows;
+
+public class ListUtil {
+
+
+ /**
+ * Asserts that two object lists are equal (same size and components in same order). If they are not, an
+ * {@link AssertionError} is thrown with the given message. It states "array" instead of list If
+ * expecteds
and actuals
are null
,
+ * they are considered equal.
+ *
+ * @param message the identifying message for the {@link AssertionError} (null
+ * okay)
+ * @param expecteds Object list or list of arrays (multi-dimensional array) with
+ * expected values.
+ * @param actuals Object list or list of arrays (multi-dimensional array) with
+ * actual values
+ */
+ public static void assertListEquals(String message, List expecteds,
+ List actuals) throws ArrayComparisonFailure {
+ Object[] expecteds_array = (expecteds == null) ? null : expecteds.toArray();
+ Object[] actuals_array = (actuals == null) ? null : actuals.toArray();
+ assertArrayEquals(message, expecteds_array, actuals_array);
+ }
+
+ /**
+ * Asserts that two object arrays are equal. If they are not, an
+ * {@link AssertionError} is thrown. If expected
and
+ * actual
are null
, they are considered
+ * equal.
+ *
+ * @param expecteds Object list or list of arrays (multi-dimensional array) with
+ * expected values
+ * @param actuals Object list or list of arrays (multi-dimensional array) with
+ * actual values
+ */
+ public static void assertListEquals(List expecteds,
+ List actuals) {
+ assertListEquals(null, expecteds, actuals);
+ }
+
+ @Test
+ public void EqualsTest() {
+ assertThrows(ArrayComparisonFailure.class, () -> assertListEquals(Arrays.asList(2L, 3L), Arrays.asList(2L, 4L)));
+ assertThrows(ArrayComparisonFailure.class, () -> assertListEquals(Arrays.asList(2L, 3L), Arrays.asList(2L)));
+ assertThrows(ArrayComparisonFailure.class, () -> assertListEquals(Arrays.asList(2L, 3L), Arrays.asList(5L)));
+ assertThrows(ArrayComparisonFailure.class, () -> assertListEquals(Arrays.asList(2L, 3L), Arrays.asList(2L, 3L, 5L)));
+ assertThrows(AssertionError.class, () -> assertListEquals(Arrays.asList(2L, 3L), null));
+ assertThrows(AssertionError.class, () -> assertListEquals(null, Arrays.asList(2L, 4L)));
+ assertListEquals(null, null);
+ assertListEquals(Arrays.asList(2L, 3L), Arrays.asList(2L, 3L));
+ }
+
+}
From 38134343a85b1dc9446f6fbbcbfbad404478996c Mon Sep 17 00:00:00 2001
From: Arthur Milchior
Date: Wed, 13 Jan 2021 20:33:00 +0100
Subject: [PATCH 034/171] NF: moving waitForAllToFinish to TaskManager
---
.../main/java/com/ichi2/anki/DeckPicker.java | 4 +--
.../java/com/ichi2/async/CollectionTask.java | 21 ----------------
.../java/com/ichi2/async/TaskManager.java | 25 +++++++++++++++++++
3 files changed, 26 insertions(+), 24 deletions(-)
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.java b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.java
index 093928c1ea51..86b5c380e62d 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.java
@@ -124,7 +124,6 @@
import com.ichi2.libanki.Utils;
import com.ichi2.libanki.importer.AnkiPackageImporter;
import com.ichi2.libanki.sched.AbstractDeckTreeNode;
-import com.ichi2.libanki.sched.DeckTreeNode;
import com.ichi2.libanki.sync.CustomSyncServerUrlException;
import com.ichi2.libanki.sync.Syncer;
import com.ichi2.libanki.utils.TimeUtils;
@@ -145,7 +144,6 @@
import java.io.File;
import java.io.FileOutputStream;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -862,7 +860,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent intent)
}
} else if (resultCode == Reviewer.RESULT_ABORT_AND_SYNC) {
Timber.i("Obtained Abort and Sync result");
- CollectionTask.waitForAllToFinish(4);
+ TaskManager.waitForAllToFinish(4);
sync();
}
} else if (requestCode == REQUEST_PATH_UPDATE) {
diff --git a/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java b/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
index 4afbcbd19cec..d687273ae8f0 100644
--- a/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
+++ b/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
@@ -111,27 +111,6 @@ public abstract static class Task {
*/
private Context mContext;
- /**
- * Block the current thread until all CollectionTasks have finished.
- * @param timeoutSeconds timeout in seconds
- * @return whether all tasks exited successfully
- */
- @SuppressWarnings("UnusedReturnValue")
- public static boolean waitForAllToFinish(Integer timeoutSeconds) {
- // HACK: This should be better - there is currently a race condition in sLatestInstance, and no means to obtain this information.
- // This should work in all reasonable cases given how few tasks we have concurrently blocking.
- boolean result;
- result = TaskManager.waitToFinish(timeoutSeconds / 4);
- ThreadUtil.sleep(10);
- result &= TaskManager.waitToFinish(timeoutSeconds / 4);
- ThreadUtil.sleep(10);
- result &= TaskManager.waitToFinish(timeoutSeconds / 4);
- ThreadUtil.sleep(10);
- result &= TaskManager.waitToFinish(timeoutSeconds / 4);
- ThreadUtil.sleep(10);
- Timber.i("Waited for all tasks to finish");
- return result;
- }
/** Cancel the current task.
* @return whether cancelling did occur.*/
diff --git a/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java b/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java
index 240a90fb3f76..f32a3d4f2f59 100644
--- a/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java
+++ b/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java
@@ -3,6 +3,8 @@
import android.content.res.Resources;
import android.os.AsyncTask;
+import com.ichi2.utils.ThreadUtil;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
@@ -141,6 +143,29 @@ public static ProgressCallback progressCallback(CollectionTask task, Resources r
}
+ /**
+ * Block the current thread until all CollectionTasks have finished.
+ * @param timeoutSeconds timeout in seconds
+ * @return whether all tasks exited successfully
+ */
+ @SuppressWarnings("UnusedReturnValue")
+ public static boolean waitForAllToFinish(Integer timeoutSeconds) {
+ // HACK: This should be better - there is currently a race condition in sLatestInstance, and no means to obtain this information.
+ // This should work in all reasonable cases given how few tasks we have concurrently blocking.
+ boolean result;
+ result = waitToFinish(timeoutSeconds / 4);
+ ThreadUtil.sleep(10);
+ result &= waitToFinish(timeoutSeconds / 4);
+ ThreadUtil.sleep(10);
+ result &= waitToFinish(timeoutSeconds / 4);
+ ThreadUtil.sleep(10);
+ result &= waitToFinish(timeoutSeconds / 4);
+ ThreadUtil.sleep(10);
+ Timber.i("Waited for all tasks to finish");
+ return result;
+ }
+
+
/**
* Helper class for allowing inner function to publish progress of an AsyncTask.
*/
From ff4c760d5a48423623b910cca37524ce21d39b17 Mon Sep 17 00:00:00 2001
From: Arthur Milchior
Date: Wed, 13 Jan 2021 20:54:32 +0100
Subject: [PATCH 035/171] NF: move addTask entirely in TaskManager
The manager is supposde to know when a task is added, and deal with it itself
---
AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java | 1 -
AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java | 1 +
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java b/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
index d687273ae8f0..32a2d81ec904 100644
--- a/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
+++ b/AnkiDroid/src/main/java/com/ichi2/async/CollectionTask.java
@@ -150,7 +150,6 @@ protected CollectionTask(Task task, TaskLi
mTask = task;
mListener = listener;
mPreviousTask = previousTask;
- TaskManager.addTask(this);
}
@Override
diff --git a/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java b/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java
index f32a3d4f2f59..a08a97d6b1f7 100644
--- a/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java
+++ b/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java
@@ -74,6 +74,7 @@ public static CollectionTask listener) {
// Start new task
CollectionTask newTask = new CollectionTask<>(task, listener, sLatestInstance);
+ addTask(newTask);
newTask.execute();
return newTask;
}
From 94aa0358e1da6a33f0f02519f4105db20dd2d249 Mon Sep 17 00:00:00 2001
From: Arthur Milchior
Date: Tue, 30 Mar 2021 05:35:50 +0200
Subject: [PATCH 036/171] NF: SingleTaskManager
Spliting TaskManager into an abstract and a concrete part allow to change no call while still allowing to switch implementation.
---
.../com/ichi2/async/SingleTaskManager.java | 166 ++++++++++++++++++
.../java/com/ichi2/async/TaskManager.java | 136 ++++++--------
2 files changed, 216 insertions(+), 86 deletions(-)
create mode 100644 AnkiDroid/src/main/java/com/ichi2/async/SingleTaskManager.java
diff --git a/AnkiDroid/src/main/java/com/ichi2/async/SingleTaskManager.java b/AnkiDroid/src/main/java/com/ichi2/async/SingleTaskManager.java
new file mode 100644
index 000000000000..2183ffe6e4ce
--- /dev/null
+++ b/AnkiDroid/src/main/java/com/ichi2/async/SingleTaskManager.java
@@ -0,0 +1,166 @@
+package com.ichi2.async;
+
+import android.os.AsyncTask;
+
+import com.ichi2.utils.ThreadUtil;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import timber.log.Timber;
+
+public class SingleTaskManager extends TaskManager {
+
+ /**
+ * Tasks which are running or waiting to run.
+ * */
+ private final List mTasks = Collections.synchronizedList(new LinkedList<>());
+
+ private void addTasks(CollectionTask task) {
+ mTasks.add(task);
+ }
+
+ @Override
+ protected boolean removeTaskConcrete(CollectionTask task) {
+ return mTasks.remove(task);
+ }
+
+ /**
+ * The most recently started {@link CollectionTask} instance.
+ */
+ private CollectionTask mLatestInstance;
+
+ protected void setLatestInstanceConcrete(CollectionTask task) {
+ mLatestInstance = task;
+ }
+
+
+
+
+ /**
+ * Starts a new {@link CollectionTask}, with no listener
+ *
+ * Tasks will be executed serially, in the order in which they are started.
+ *
+ * This method must be called on the main thread.
+ *
+ * @param task the task to execute
+ * @return the newly created task
+ */
+ @Override
+ public CollectionTask launchCollectionTaskConcrete(CollectionTask.Task task) {
+ return launchCollectionTask(task, null);
+ }
+
+
+
+ /**
+ * Starts a new {@link CollectionTask}, with a listener provided for callbacks during execution
+ *
+ * Tasks will be executed serially, in the order in which they are started.
+ *
+ * This method must be called on the main thread.
+ *
+ * @param task the task to execute
+ * @param listener to the status and result of the task, may be null
+ * @return the newly created task
+ */
+ public CollectionTask
+ launchCollectionTaskConcrete(@NonNull CollectionTask.Task task,
+ @Nullable TaskListener listener) {
+ // Start new task
+ CollectionTask newTask = new CollectionTask<>(task, listener, mLatestInstance);
+ addTasks(newTask);
+ newTask.execute();
+ return newTask;
+ }
+
+
+ /**
+ * Block the current thread until the currently running CollectionTask instance (if any) has finished.
+ */
+ public void waitToFinishConcrete() {
+ waitToFinish(null);
+ }
+
+ /**
+ * Block the current thread until the currently running CollectionTask instance (if any) has finished.
+ * @param timeoutSeconds timeout in seconds
+ * @return whether or not the previous task was successful or not
+ */
+ @Override
+ public boolean waitToFinishConcrete(Integer timeoutSeconds) {
+ try {
+ if ((mLatestInstance != null) && (mLatestInstance.getStatus() != AsyncTask.Status.FINISHED)) {
+ Timber.d("CollectionTask: waiting for task %s to finish...", mLatestInstance.getTask().getClass());
+ if (timeoutSeconds != null) {
+ mLatestInstance.get(timeoutSeconds, TimeUnit.SECONDS);
+ } else {
+ mLatestInstance.get();
+ }
+
+ }
+ return true;
+ } catch (Exception e) {
+ Timber.e(e, "Exception waiting for task to finish");
+ return false;
+ }
+ }
+
+
+ /** Cancel the current task only if it's of type taskType */
+ @Override
+ public void cancelCurrentlyExecutingTaskConcrete() {
+ CollectionTask latestInstance = mLatestInstance;
+ if (latestInstance != null) {
+ if (latestInstance.safeCancel()) {
+ Timber.i("Cancelled task %s", latestInstance.getTask().getClass());
+ }
+ }
+ }
+
+ /** Cancel all tasks of type taskType*/
+ public void cancelAllTasksConcrete(Class taskType) {
+ int count = 0;
+ // safeCancel modifies mTasks, so iterate over a concrete copy
+ for (CollectionTask task: new ArrayList<>(mTasks)) {
+ if (task.getTask().getClass() != taskType) {
+ continue;
+ }
+ if (task.safeCancel()) {
+ count++;
+ }
+ }
+ if (count > 0) {
+ Timber.i("Cancelled %d instances of task %s", count, taskType);
+ }
+ }
+
+ /**
+ * Block the current thread until all CollectionTasks have finished.
+ * @param timeoutSeconds timeout in seconds
+ * @return whether all tasks exited successfully
+ */
+ @SuppressWarnings("UnusedReturnValue")
+ @Override
+ public boolean waitForAllToFinishConcrete(Integer timeoutSeconds) {
+ // HACK: This should be better - there is currently a race condition in sLatestInstance, and no means to obtain this information.
+ // This should work in all reasonable cases given how few tasks we have concurrently blocking.
+ boolean result;
+ result = waitToFinish(timeoutSeconds / 4);
+ ThreadUtil.sleep(10);
+ result &= waitToFinish(timeoutSeconds / 4);
+ ThreadUtil.sleep(10);
+ result &= waitToFinish(timeoutSeconds / 4);
+ ThreadUtil.sleep(10);
+ result &= waitToFinish(timeoutSeconds / 4);
+ ThreadUtil.sleep(10);
+ Timber.i("Waited for all tasks to finish");
+ return result;
+ }
+}
diff --git a/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java b/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java
index a08a97d6b1f7..faf215e66d96 100644
--- a/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java
+++ b/AnkiDroid/src/main/java/com/ichi2/async/TaskManager.java
@@ -3,6 +3,7 @@
import android.content.res.Resources;
import android.os.AsyncTask;
+import com.ichi2.libanki.Collection;
import com.ichi2.utils.ThreadUtil;
import java.util.ArrayList;
@@ -13,34 +14,28 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import timber.log.Timber;
-public class TaskManager {
+public abstract class TaskManager {
+ @NonNull private static TaskManager sTaskManager = new SingleTaskManager();
/**
- * Tasks which are running or waiting to run.
- * */
- private static final List sTasks = Collections.synchronizedList(new LinkedList<>());
-
- protected static void addTask(CollectionTask task) {
- sTasks.add(task);
+ * @param tm The new task manager
+ * @return The previous one. It may still have tasks running
+ */
+ @VisibleForTesting
+ public static TaskManager setTaskManager(TaskManager tm) {
+ TaskManager previous = sTaskManager;
+ sTaskManager = tm;
+ return previous;
}
protected static boolean removeTask(CollectionTask task) {
- return sTasks.remove(task);
- }
-
-
- /**
- * The most recently started {@link CollectionTask} instance.
- */
- private static CollectionTask sLatestInstance;
-
- protected static void setLatestInstance(CollectionTask task) {
- sLatestInstance = task;
+ return sTaskManager.removeTaskConcrete(task);
}
-
+ protected abstract boolean removeTaskConcrete(CollectionTask task);
/**
* Starts a new {@link CollectionTask}, with no listener
@@ -52,12 +47,19 @@ protected static void setLatestInstance(CollectionTask task) {
* @param task the task to execute
* @return the newly created task
*/
+ protected static void setLatestInstance(CollectionTask task) {
+ sTaskManager.setLatestInstanceConcrete(task);
+ }
+
public static CollectionTask launchCollectionTask(CollectionTask.Task task) {
- return launchCollectionTask(task, null);
+ return sTaskManager.launchCollectionTaskConcrete(task);
}
+ public abstract CollectionTask launchCollectionTaskConcrete(CollectionTask.Task task);
+ protected abstract void setLatestInstanceConcrete(CollectionTask task);
+
/**
* Starts a new {@link CollectionTask}, with a listener provided for callbacks during execution
*
@@ -70,22 +72,22 @@ public static CollectionTask CollectionTask
- launchCollectionTask(@NonNull CollectionTask.Task task,
- @Nullable TaskListener listener) {
- // Start new task
- CollectionTask newTask = new CollectionTask<>(task, listener, sLatestInstance);
- addTask(newTask);
- newTask.execute();
- return newTask;
+ launchCollectionTask(@NonNull CollectionTask.Task task,
+ @Nullable TaskListener listener) {
+ return sTaskManager.launchCollectionTaskConcrete(task, listener);
}
+ public abstract CollectionTask
+ launchCollectionTaskConcrete(@NonNull CollectionTask.Task task,
+ @Nullable TaskListener listener);
/**
* Block the current thread until the currently running CollectionTask instance (if any) has finished.
*/
public static void waitToFinish() {
- waitToFinish(null);
- }
+ sTaskManager.waitToFinishConcrete();
+ };
+ public abstract void waitToFinishConcrete();
/**
* Block the current thread until the currently running CollectionTask instance (if any) has finished.
@@ -93,56 +95,22 @@ public static void waitToFinish() {
* @return whether or not the previous task was successful or not
*/
public static boolean waitToFinish(Integer timeoutSeconds) {
- try {
- if ((sLatestInstance != null) && (sLatestInstance.getStatus() != AsyncTask.Status.FINISHED)) {
- Timber.d("CollectionTask: waiting for task %s to finish...", sLatestInstance.getTask().getClass());
- if (timeoutSeconds != null) {
- sLatestInstance.get(timeoutSeconds, TimeUnit.SECONDS);
- } else {
- sLatestInstance.get();
- }
-
- }
- return true;
- } catch (Exception e) {
- Timber.e(e, "Exception waiting for task to finish");
- return false;
- }
- }
+ return sTaskManager.waitToFinishConcrete(timeoutSeconds);
+ };
+ public abstract boolean waitToFinishConcrete(Integer timeoutSeconds);
/** Cancel the current task only if it's of type taskType */
public static void cancelCurrentlyExecutingTask() {
- CollectionTask latestInstance = sLatestInstance;
- if (latestInstance != null) {
- if (latestInstance.safeCancel()) {
- Timber.i("Cancelled task %s", latestInstance.getTask().getClass());
- }
- }
+ sTaskManager.cancelCurrentlyExecutingTaskConcrete();
}
+ public abstract void cancelCurrentlyExecutingTaskConcrete();
/** Cancel all tasks of type taskType*/
public static void cancelAllTasks(Class taskType) {
- int count = 0;
- // safeCancel modifies sTasks, so iterate over a concrete copy
- for (CollectionTask task: new ArrayList<>(sTasks)) {
- if (task.getTask().getClass() != taskType) {
- continue;
- }
- if (task.safeCancel()) {
- count++;
- }
- }
- if (count > 0) {
- Timber.i("Cancelled %d instances of task %s", count, taskType);
- }
- }
-
-
- public static ProgressCallback progressCallback(CollectionTask task, Resources res) {
- return new ProgressCallback(task, res);
+ sTaskManager.cancelAllTasksConcrete(taskType);
}
-
+ public abstract void cancelAllTasksConcrete(Class taskType);
/**
* Block the current thread until all CollectionTasks have finished.
@@ -151,25 +119,16 @@ public static ProgressCallback progressCallback(CollectionTask task, Resources r
*/
@SuppressWarnings("UnusedReturnValue")
public static boolean waitForAllToFinish(Integer timeoutSeconds) {
- // HACK: This should be better - there is currently a race condition in sLatestInstance, and no means to obtain this information.
- // This should work in all reasonable cases given how few tasks we have concurrently blocking.
- boolean result;
- result = waitToFinish(timeoutSeconds / 4);
- ThreadUtil.sleep(10);
- result &= waitToFinish(timeoutSeconds / 4);
- ThreadUtil.sleep(10);
- result &= waitToFinish(timeoutSeconds / 4);
- ThreadUtil.sleep(10);
- result &= waitToFinish(timeoutSeconds / 4);
- ThreadUtil.sleep(10);
- Timber.i("Waited for all tasks to finish");
- return result;
+ return sTaskManager.waitToFinishConcrete(timeoutSeconds);
}
+ public abstract boolean waitForAllToFinishConcrete(Integer timeoutSeconds);
- /**
- * Helper class for allowing inner function to publish progress of an AsyncTask.
- */
+
+
+ /**
+ * Helper class for allowing inner function to publish progress of an AsyncTask.
+ */
public static class ProgressCallback