Refresh dictionary list on user action
app/build.gradle
| 21 | 21 | dependencies { | |
| 22 | 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) | |
| 23 | 23 | implementation 'androidx.appcompat:appcompat:1.1.0' | |
| 24 | - | implementation 'androidx.preference:preference:1.1.1' | |
| 25 | 24 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' | |
| 26 | - | implementation 'com.google.android.material:material:1.1.0' | |
| 25 | + | implementation 'androidx.legacy:legacy-support-v4:1.0.0' | |
| 26 | + | implementation 'androidx.preference:preference:1.1.1' | |
| 27 | + | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" | |
| 27 | 28 | implementation 'com.andree-surya:moji4j:1.0.0' | |
| 28 | 29 | implementation 'com.google.android:flexbox:2.0.1' | |
| 30 | + | implementation 'com.google.android.material:material:1.1.0' | |
| 29 | 31 | implementation project(path: ':furiganatextview') | |
| 30 | - | implementation 'androidx.legacy:legacy-support-v4:1.0.0' | |
| 31 | 32 | testImplementation 'junit:junit:4.12' | |
| 32 | 33 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' | |
| 33 | 34 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' |
app/src/main/java/eu/lepiller/nani/DictionaryActivity.java
| 1 | 1 | package eu.lepiller.nani; | |
| 2 | 2 | ||
| 3 | 3 | import android.content.Intent; | |
| 4 | + | import android.os.AsyncTask; | |
| 4 | 5 | import android.os.Bundle; | |
| 5 | 6 | import androidx.appcompat.app.AppCompatActivity; | |
| 6 | 7 | import androidx.appcompat.widget.Toolbar; | |
| 8 | + | import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; | |
| 9 | + | ||
| 7 | 10 | import android.view.View; | |
| 8 | 11 | import android.widget.AdapterView; | |
| 9 | 12 | import android.widget.ListView; | |
| 10 | 13 | ||
| 14 | + | import com.google.android.material.snackbar.Snackbar; | |
| 15 | + | ||
| 16 | + | import java.io.File; | |
| 17 | + | import java.io.FileOutputStream; | |
| 18 | + | import java.io.IOException; | |
| 19 | + | import java.io.InputStream; | |
| 20 | + | import java.net.HttpURLConnection; | |
| 21 | + | import java.net.MalformedURLException; | |
| 22 | + | import java.net.URL; | |
| 11 | 23 | import java.util.ArrayList; | |
| 12 | 24 | ||
| 13 | 25 | import eu.lepiller.nani.dictionary.DictionariesAdapter; | |
… | |||
| 39 | 51 | startActivityForResult(intent, DICO_REQUEST); | |
| 40 | 52 | } | |
| 41 | 53 | }); | |
| 54 | + | ||
| 55 | + | final SwipeRefreshLayout refresher = findViewById(R.id.dictionary_refresh_layout); | |
| 56 | + | refresher.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { | |
| 57 | + | @Override | |
| 58 | + | public void onRefresh() { | |
| 59 | + | new DownloadTask(refresher).execute(); | |
| 60 | + | } | |
| 61 | + | }); | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | private class DownloadTask extends AsyncTask<String, Integer, Integer> { | |
| 65 | + | private SwipeRefreshLayout refresher; | |
| 66 | + | ||
| 67 | + | DownloadTask(SwipeRefreshLayout r) { | |
| 68 | + | refresher = r; | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | @Override | |
| 72 | + | protected Integer doInBackground(String... strings) { | |
| 73 | + | try { | |
| 74 | + | URL url = DictionaryFactory.getListUrl(); | |
| 75 | + | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); | |
| 76 | + | connection.connect(); | |
| 77 | + | if(connection.getResponseCode() == HttpURLConnection.HTTP_OK) { | |
| 78 | + | InputStream input = connection.getInputStream(); | |
| 79 | + | File file = DictionaryFactory.getListFile(); | |
| 80 | + | file.getParentFile().mkdirs(); | |
| 81 | + | int count = 0; | |
| 82 | + | byte[] data = new byte[4096]; | |
| 83 | + | FileOutputStream output = new FileOutputStream(file); | |
| 84 | + | while((count = input.read(data)) != -1) { | |
| 85 | + | if (isCancelled()) { | |
| 86 | + | input.close(); | |
| 87 | + | return null; | |
| 88 | + | } | |
| 89 | + | output.write(data, 0, count); | |
| 90 | + | } | |
| 91 | + | } else { | |
| 92 | + | return R.string.error_dico_not_found; | |
| 93 | + | } | |
| 94 | + | } catch (MalformedURLException e) { | |
| 95 | + | e.printStackTrace(); | |
| 96 | + | return R.string.error_dico_url; | |
| 97 | + | } catch (IOException e) { | |
| 98 | + | e.printStackTrace(); | |
| 99 | + | return R.string.error_dico_io; | |
| 100 | + | } | |
| 101 | + | return null; | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | @Override | |
| 105 | + | protected void onPostExecute(Integer i) { | |
| 106 | + | super.onPostExecute(i); | |
| 107 | + | refresher.setRefreshing(false); | |
| 108 | + | adapter.notifyDataSetChanged(); | |
| 109 | + | if(i == null) | |
| 110 | + | return; | |
| 111 | + | Snackbar.make(findViewById(R.id.dictionary_view), getString(i), | |
| 112 | + | Snackbar.LENGTH_LONG).show(); | |
| 113 | + | } | |
| 42 | 114 | } | |
| 43 | 115 | ||
| 44 | 116 | @Override | |
| 45 | 117 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { | |
| 46 | - | if(requestCode == DICO_REQUEST) { | |
| 118 | + | super.onActivityResult(requestCode, resultCode, data); | |
| 119 | + | if (requestCode == DICO_REQUEST) { | |
| 47 | 120 | adapter.notifyDataSetChanged(); | |
| 48 | 121 | } | |
| 49 | 122 | } | |
app/src/main/java/eu/lepiller/nani/dictionary/DictionaryFactory.java
| 2 | 2 | ||
| 3 | 3 | import android.content.Context; | |
| 4 | 4 | ||
| 5 | + | import java.io.File; | |
| 6 | + | import java.net.MalformedURLException; | |
| 7 | + | import java.net.URL; | |
| 5 | 8 | import java.util.ArrayList; | |
| 6 | 9 | ||
| 7 | 10 | import eu.lepiller.nani.R; | |
… | |||
| 10 | 13 | public class DictionaryFactory { | |
| 11 | 14 | private static DictionaryFactory instance; | |
| 12 | 15 | private static ArrayList<Dictionary> dictionaries; | |
| 16 | + | private static File cacheDir; | |
| 13 | 17 | ||
| 14 | 18 | private DictionaryFactory(Context context) { | |
| 19 | + | cacheDir = context.getCacheDir(); | |
| 15 | 20 | dictionaries = new ArrayList<>(); | |
| 16 | 21 | dictionaries.add(new RadicalDict("RadK", | |
| 17 | 22 | context.getString(R.string.dico_radk), | |
| 18 | 23 | context.getString(R.string.dico_radk_long), | |
| 19 | - | context.getCacheDir(), | |
| 24 | + | cacheDir, | |
| 20 | 25 | "https://nani.lepiller.eu/dicos/radicals.nani")); | |
| 21 | 26 | dictionaries.add(new JMDict("JMdict_e", | |
| 22 | 27 | context.getString(R.string.dico_jmdict_e), | |
| 23 | 28 | context.getString(R.string.dico_jmdict_long), | |
| 24 | - | context.getCacheDir(), | |
| 29 | + | cacheDir, | |
| 25 | 30 | "https://nani.lepiller.eu/dicos/JMdict_e.nani")); | |
| 26 | 31 | dictionaries.add(new JMDict("JMdict_dut", | |
| 27 | 32 | context.getString(R.string.dico_jmdict_dut), | |
| 28 | 33 | context.getString(R.string.dico_jmdict_long), | |
| 29 | - | context.getCacheDir(), | |
| 34 | + | cacheDir, | |
| 30 | 35 | "https://nani.lepiller.eu/dicos/JMdict_dut.nani")); | |
| 31 | 36 | dictionaries.add(new JMDict("JMdict_fre", | |
| 32 | 37 | context.getString(R.string.dico_jmdict_fre), | |
| 33 | 38 | context.getString(R.string.dico_jmdict_long), | |
| 34 | - | context.getCacheDir(), | |
| 39 | + | cacheDir, | |
| 35 | 40 | "https://nani.lepiller.eu/dicos/JMdict_fre.nani")); | |
| 36 | 41 | dictionaries.add(new JMDict("JMdict_ger", | |
| 37 | 42 | context.getString(R.string.dico_jmdict_ger), | |
| 38 | 43 | context.getString(R.string.dico_jmdict_long), | |
| 39 | - | context.getCacheDir(), | |
| 44 | + | cacheDir, | |
| 40 | 45 | "https://nani.lepiller.eu/dicos/JMdict_ger.nani")); | |
| 41 | 46 | dictionaries.add(new JMDict("JMdict_hun", | |
| 42 | 47 | context.getString(R.string.dico_jmdict_hun), | |
| 43 | 48 | context.getString(R.string.dico_jmdict_long), | |
| 44 | - | context.getCacheDir(), | |
| 49 | + | cacheDir, | |
| 45 | 50 | "https://nani.lepiller.eu/dicos/JMdict_hun.nani")); | |
| 46 | 51 | dictionaries.add(new JMDict("JMdict_rus", | |
| 47 | 52 | context.getString(R.string.dico_jmdict_rus), | |
| 48 | 53 | context.getString(R.string.dico_jmdict_long), | |
| 49 | - | context.getCacheDir(), | |
| 54 | + | cacheDir, | |
| 50 | 55 | "https://nani.lepiller.eu/dicos/JMdict_rus.nani")); | |
| 51 | 56 | dictionaries.add(new JMDict("JMdict_slv", | |
| 52 | 57 | context.getString(R.string.dico_jmdict_slv), | |
| 53 | 58 | context.getString(R.string.dico_jmdict_long), | |
| 54 | - | context.getCacheDir(), | |
| 59 | + | cacheDir, | |
| 55 | 60 | "https://nani.lepiller.eu/dicos/JMdict_slv.nani")); | |
| 56 | 61 | dictionaries.add(new JMDict("JMdict_spa", | |
| 57 | 62 | context.getString(R.string.dico_jmdict_spa), | |
| 58 | 63 | context.getString(R.string.dico_jmdict_long), | |
| 59 | - | context.getCacheDir(), | |
| 64 | + | cacheDir, | |
| 60 | 65 | "https://nani.lepiller.eu/dicos/JMdict_spa.nani")); | |
| 61 | 66 | dictionaries.add(new JMDict("JMdict_swe", | |
| 62 | 67 | context.getString(R.string.dico_jmdict_swe), | |
| 63 | 68 | context.getString(R.string.dico_jmdict_long), | |
| 64 | - | context.getCacheDir(), | |
| 69 | + | cacheDir, | |
| 65 | 70 | "https://nani.lepiller.eu/dicos/JMdict_swe.nani")); | |
| 66 | 71 | } | |
| 67 | 72 | ||
… | |||
| 114 | 119 | ||
| 115 | 120 | throw new NoDictionaryException(); | |
| 116 | 121 | } | |
| 122 | + | ||
| 123 | + | public static URL getListUrl() throws MalformedURLException { | |
| 124 | + | return new URL("https://nani.lepiller.eu/dicos/list"); | |
| 125 | + | } | |
| 126 | + | ||
| 127 | + | public static File getListFile() { | |
| 128 | + | return new File(cacheDir + "/list"); | |
| 129 | + | } | |
| 117 | 130 | } | |
app/src/main/res/layout/content_dictionary.xml
| 8 | 8 | tools:context=".DictionaryActivity" | |
| 9 | 9 | tools:showIn="@layout/activity_dictionary"> | |
| 10 | 10 | ||
| 11 | - | <ListView | |
| 12 | - | android:id="@+id/dictionary_view" | |
| 13 | - | android:layout_width="395dp" | |
| 14 | - | android:layout_height="659dp" | |
| 15 | - | android:layout_marginStart="8dp" | |
| 16 | - | android:layout_marginLeft="8dp" | |
| 17 | - | android:layout_marginTop="32dp" | |
| 18 | - | android:layout_marginEnd="8dp" | |
| 19 | - | android:layout_marginRight="8dp" | |
| 20 | - | android:layout_marginBottom="8dp" /> | |
| 11 | + | <androidx.swiperefreshlayout.widget.SwipeRefreshLayout | |
| 12 | + | android:layout_width="match_parent" | |
| 13 | + | android:layout_height="wrap_content" | |
| 14 | + | android:id="@+id/dictionary_refresh_layout"> | |
| 15 | + | ||
| 16 | + | <ListView | |
| 17 | + | android:id="@+id/dictionary_view" | |
| 18 | + | android:layout_width="395dp" | |
| 19 | + | android:layout_height="659dp" | |
| 20 | + | android:layout_marginStart="8dp" | |
| 21 | + | android:layout_marginLeft="8dp" | |
| 22 | + | android:layout_marginTop="32dp" | |
| 23 | + | android:layout_marginEnd="8dp" | |
| 24 | + | android:layout_marginRight="8dp" | |
| 25 | + | android:layout_marginBottom="8dp" /> | |
| 26 | + | </androidx.swiperefreshlayout.widget.SwipeRefreshLayout> | |
| 21 | 27 | </LinearLayout> | |
| 21 | 27 | = | |
| 22 | 28 | = | \ No newline at end of file |
app/src/main/res/values/strings.xml
| 57 | 57 | <string name="dico_radk_long">This dictionary allows you to enter kanji by selecting some of its components. Tap the water component button on the bottom of the screen | |
| 58 | 58 | to access the kanji selection by component view.</string> | |
| 59 | 59 | ||
| 60 | + | <string name="error_dico_url">Error fetching dictionary list: malformed URL.</string> | |
| 61 | + | <string name="error_dico_io">Error fetching dictionary list: cannot write to cache file.</string> | |
| 62 | + | <string name="error_dico_not_found">Error fetching dictionary list: cannot find it on server.</string> | |
| 63 | + | ||
| 60 | 64 | <!-- Result view --> | |
| 61 | 65 | <string name="sense_number">%d.</string> | |
| 62 | 66 | <string name="sense_separator">"; "</string> |