Code cleanup following IDE advice

Julien LepillerSun May 31 20:59:07+0200 2020

19e038a

Code cleanup following IDE advice

app/src/main/java/eu/lepiller/nani/DictionaryActivity.java

77
import androidx.appcompat.widget.Toolbar;
88
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
99
10+
import android.util.Log;
1011
import android.view.View;
1112
import android.widget.AdapterView;
1213
import android.widget.ListView;

2829
2930
public class DictionaryActivity extends AppCompatActivity {
3031
    static final int DICO_REQUEST = 1;
32+
    static final String TAG = "DLIST";
3133
    DictionariesAdapter adapter;
3234
    ArrayList<Dictionary> dictionaries;
3335

8587
                if(connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
8688
                    InputStream input = connection.getInputStream();
8789
                    File file = DictionaryFactory.getListFile();
88-
                    file.getParentFile().mkdirs();
89-
                    int count = 0;
90+
                    File parent = file.getParentFile();
91+
                    if(parent != null && !parent.exists())
92+
                        if(!parent.mkdirs())
93+
                            Log.w(TAG, "Could not create parent directory for " + file);
94+
                    int count;
9095
                    byte[] data = new byte[4096];
9196
                    FileOutputStream output = new FileOutputStream(file);
9297
                    while((count = input.read(data)) != -1) {

app/src/main/java/eu/lepiller/nani/DictionaryDownloadActivity.java

11
package eu.lepiller.nani;
22
33
import android.app.NotificationManager;
4-
import android.content.Context;
54
import android.graphics.drawable.Drawable;
65
import android.os.AsyncTask;
76
import android.os.Build;

1312
import android.util.Log;
1413
import android.util.Pair;
1514
import android.view.View;
16-
import android.widget.ImageButton;
1715
import android.widget.ImageView;
1816
import android.widget.LinearLayout;
1917
import android.widget.ProgressBar;

7977
    protected void onCreate(Bundle savedInstanceState) {
8078
        super.onCreate(savedInstanceState);
8179
        setContentView(R.layout.activity_dictionary_download);
80+
        Bundle extras = getIntent().getExtras();
8281
83-
        int position = getIntent().getExtras().getInt("dico");
82+
        // TODO: this will open the first dictionary on error, we probably want to display a proper
83+
        // error instead.
84+
        int position = 0;
85+
        if(extras != null)
86+
            position = extras.getInt("dico");
8487
8588
        d = DictionaryFactory.get(position);
8689

198201
                    if(connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
199202
                        input = connection.getInputStream();
200203
                        file = new File(cacheFile + ".sha256");
201-
                        file.getParentFile().mkdirs();
204+
                        if(file.getParentFile() == null || (!file.getParentFile().exists() && !file.getParentFile().mkdirs()))
205+
                            Log.w(TAG, "could not create parent of " + file);
202206
                        File old_file = new File(file + ".old");
203207
                        if(old_file.exists()) {
204-
                            old_file.delete();
208+
                            if(!old_file.delete())
209+
                                Log.w(TAG, "could not delete file " + old_file);
205210
                        }
206211
                        if(file.exists()) {
207-
                            file.renameTo(old_file);
212+
                            if(!file.renameTo(old_file))
213+
                                Log.w(TAG, "could not rename "+ file + " to " + old_file);
208214
                        }
209215
                        output = new FileOutputStream(file);
210216
                        while((count = input.read(data)) != -1) {

225231
                                d.remove();
226232
                            }
227233
228-
                            old_file.delete();
234+
                            if(!old_file.delete())
235+
                                Log.w(TAG, "could not delete file " + old_file);
229236
                        }
230237
                    }
231238
232239
                    // .sha256 file now downloaded
233240
                    url = new URL(uri);
234241
                    file = temporaryFile;
235-
                    file.getParentFile().mkdirs();
242+
                    if(file.getParentFile() == null || (!file.getParentFile().exists() && !file.getParentFile().mkdirs()))
243+
                        Log.w(TAG, "could not create parent of " + file);
236244
                    long expectedLength = -1;
237245
                    boolean acceptRanges = false;
238246
                    if(file.exists()) {

246254
                            acceptRanges = connection.getHeaderFields().containsKey("accept-ranges");
247255
                            if (acceptRanges) {
248256
                                List<String> headers = connection.getHeaderFields().get("accept-ranges");
249-
                                for(String h: headers) {
250-
                                    if(h.toLowerCase().compareTo("none") == 0)
251-
                                        acceptRanges = false;
257+
                                if(headers != null) {
258+
                                    for (String h : headers) {
259+
                                        if (h.toLowerCase().compareTo("none") == 0)
260+
                                            acceptRanges = false;
261+
                                    }
252262
                                }
253263
                            }
254264
                            Log.d(TAG, "Can do range? " + acceptRanges);

app/src/main/java/eu/lepiller/nani/MainActivity.java

6060
                    "Nani's dictionary download notification", NotificationManager.IMPORTANCE_DEFAULT);
6161
            NotificationManager manager =
6262
                    (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
63-
            manager.createNotificationChannel(channel);
63+
            if(manager != null)
64+
                manager.createNotificationChannel(channel);
6465
        }
6566
6667
        PreferenceManager.setDefaultValues(this, R.xml.preferences, false);

216217
            });
217218
        }
218219
        savedResults = searchResult;
219-
        showResults(searchResult);
220+
        if(searchResult != null)
221+
            showResults(searchResult);
220222
    }
221223
222224
    void showResults(ArrayList<Result> searchResult) {

app/src/main/java/eu/lepiller/nani/RadicalSelectorView.java

11
package eu.lepiller.nani;
22
33
import android.content.Context;
4-
import android.content.SharedPreferences;
54
import android.os.AsyncTask;
65
import android.os.Build;
76
import android.util.AttributeSet;

1716
import com.google.android.flexbox.FlexWrap;
1817
import com.google.android.flexbox.FlexboxLayout;
1918
19+
import java.text.NumberFormat;
2020
import java.util.ArrayList;
21+
import java.util.Arrays;
2122
import java.util.List;
2223
import java.util.Map;
2324
import java.util.Set;

2728
2829
public class RadicalSelectorView extends LinearLayout implements OnTaskCompleted<Map<Integer, List<String>>>,
2930
        OnOtherTaskCompleted<Pair<Map<Integer, List<String>>, Set<String>>> {
31+
    static private String TAG = "RSV";
3032
    private Button close_button;
3133
    private LinearLayout kanji_row1, kanji_row2;
3234
    private LinearLayout radical_table;

4648
    public RadicalSelectorView(Context context, AttributeSet attrs, int defStyle) {
4749
        super(context, attrs, defStyle);
4850
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
51+
        if(inflater == null) {
52+
            Log.e(TAG, "Inflater is null!");
53+
            return;
54+
        }
4955
        inflater.inflate(R.layout.content_radicals, this);
5056
        this.setOrientation(VERTICAL);
5157
    }

7076
        resizeRadicals();
7177
    }
7278
73-
    public float getRadFontSize() {
79+
    public int getRadFontSize() {
7480
        return radSize / 8;
7581
    }
7682

8187
8288
        for (int stroke : radicals.keySet()) {
8389
            TextView strokeView = new TextView(getContext());
84-
            strokeView.setText(Integer.toString(stroke));
90+
            NumberFormat nf = NumberFormat.getInstance();
91+
            strokeView.setText(nf.format(stroke));
8592
            radical_table.addView(strokeView);
8693
8794
            FlexboxLayout box = new FlexboxLayout(getContext());
8895
            radical_table.addView(box);
8996
9097
            List<String> lst = radicals.get(stroke);
98+
            if(lst == null)
99+
                continue;
100+
91101
            for (final String radical : lst) {
92102
                ToggleButton radicalButton = new ToggleButton(getContext());
93103
                radicalButton.setTextOff(radical);

169179
        @Override
170180
        protected Pair<Map<Integer, List<String>>, Set<String>> doInBackground(String... sInput) {
171181
            try {
172-
                List<String> radicals = new ArrayList<>();
173-
                for(String s: sInput) radicals.add(s);
182+
                List<String> radicals = Arrays.asList(sInput);
174183
                return new Pair<>(dict.match(radicals), dict.availableRadicals(radicals));
175184
            } catch (IncompatibleFormatException e) {
176185
                e.printStackTrace();

203212
        int total = 0;
204213
        int number = 0;
205214
        for (int stroke : kanji.keySet()) {
206-
            total += kanji.get(stroke).size();
215+
            List<String> s = kanji.get(stroke);
216+
            if(s != null)
217+
                total += s.size();
207218
        }
208219
        for (int stroke : kanji.keySet()) {
209220
            TextView strokeView = new TextView(getContext());
210-
            strokeView.setText(Integer.toString(stroke));
221+
            NumberFormat nf = NumberFormat.getInstance();
222+
            strokeView.setText(nf.format(stroke));
211223
            strokeView.setLayoutParams(new LayoutParams(66, 66));
212224
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
213225
                strokeView.setTextColor(getContext().getColor(R.color.colorAccent));

217229
            }
218230
            row.addView(strokeView);
219231
220-
            for (String k : kanji.get(stroke)) {
232+
            List<String> kanjis = kanji.get(stroke);
233+
            if(kanjis == null)
234+
                continue;
235+
236+
            for (String k : kanjis) {
221237
                number++;
222238
                Button kanji_button = new Button(getContext());
223239
                kanji_button.setText(k);

app/src/main/java/eu/lepiller/nani/SearchResult.java

55
import eu.lepiller.nani.dictionary.DictionaryException;
66
import eu.lepiller.nani.result.Result;
77
8-
public class SearchResult {
8+
class SearchResult {
99
    private ArrayList<Result> results;
1010
    private DictionaryException exception;
1111
    private boolean converted;

app/src/main/java/eu/lepiller/nani/SettingsFragment.java

55
import androidx.fragment.app.Fragment;
66
import androidx.preference.PreferenceFragmentCompat;
77
8-
import android.view.LayoutInflater;
9-
import android.view.View;
10-
import android.view.ViewGroup;
11-
import android.widget.TextView;
12-
138
/**
149
 * A simple {@link Fragment} subclass.
1510
 */

app/src/main/java/eu/lepiller/nani/dictionary/DictionariesAdapter.java

99
import android.widget.ImageView;
1010
import android.widget.TextView;
1111
12+
import androidx.annotation.NonNull;
13+
1214
import java.text.NumberFormat;
1315
import java.util.ArrayList;
1416
1517
import eu.lepiller.nani.R;
1618
1719
public class DictionariesAdapter extends ArrayAdapter<Dictionary> {
18-
    Context context;
20+
    private Context context;
1921
    public DictionariesAdapter(Context context, ArrayList<Dictionary> dictionaries) {
2022
        super(context, 0, dictionaries);
2123
        this.context = context;
2224
    }
2325
24-
    @Override
25-
    public View getView(int position, View convertView, ViewGroup parent) {
26+
    @Override @NonNull
27+
    public View getView(int position, View convertView, @NonNull ViewGroup parent) {
2628
        // Get the data item for this position
2729
        Dictionary dictionary = getItem(position);
2830

4547
        // Populate the data into the template view using the data object
4648
        Drawable icon = dictionary.getDrawable(context);
4749
        if(icon != null) {
48-
            icon = icon.getConstantState().newDrawable().mutate();
49-
            if(!dictionary.isDownloaded())
50-
                icon.setAlpha(64);
51-
            else
52-
                icon.setAlpha(255);
50+
            Drawable.ConstantState state = icon.getConstantState();
51+
            if(state != null) {
52+
                icon = state.newDrawable().mutate();
53+
                if (!dictionary.isDownloaded())
54+
                    icon.setAlpha(64);
55+
                else
56+
                    icon.setAlpha(255);
57+
            }
5358
            icon_view.setImageDrawable(icon);
5459
        }
5560

app/src/main/java/eu/lepiller/nani/dictionary/Dictionary.java

33
import android.content.Context;
44
import android.graphics.drawable.Drawable;
55
import android.os.Build;
6-
import android.util.Log;
76
import android.util.Pair;
87
98
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;

1413
import java.io.FileReader;
1514
import java.io.IOException;
1615
import java.math.BigInteger;
17-
import java.net.URL;
1816
import java.security.MessageDigest;
1917
import java.security.NoSuchAlgorithmException;
2018
import java.util.Map;

2523
    private int expectedFileSize;
2624
    private int expectedEntries;
2725
    private String sha256;
28-
    File file, temporaryFile;
26+
    private File file, temporaryFile;
2927
3028
    Dictionary(String n, String descr, String fullDescr, File cacheDir, int fileSize, int entries, String hash) {
3129
        name = n;

5048
    public int getExpectedFileSize() {
5149
        return expectedFileSize;
5250
    }
53-
    public int getExpectedEntries() {
51+
    int getExpectedEntries() {
5452
        return expectedEntries;
5553
    }
56-
    public String getSha256() {
54+
    String getSha256() {
5755
        return sha256;
5856
    }
5957
    abstract public boolean isUpToDate();

7270
7371
    abstract public int size();
7472
75-
    // Used for downloads: tells what size we currently have downloaded
76-
    abstract int currentSize();
77-
7873
    public abstract Map<String, Pair<File, File>> getDownloads();
7974
8075
    public Drawable getDrawable(Context context) {

9489
9590
    public static String readSha256FromFile(File file) throws IOException {
9691
        StringBuilder sb = new StringBuilder();
97-
        char data[] = new char[4096];
92+
        char[] data = new char[4096];
9893
        FileReader fr = new FileReader(file);
9994
        int count;
10095
        while((count = fr.read(data)) != -1) {

10499
        return sb.toString().trim();
105100
    }
106101
107-
    public static String sha256OfFile(File file) {
102+
    static String sha256OfFile(File file) {
108103
        MessageDigest digest=null;
109104
        try {
110105
            digest = MessageDigest.getInstance("SHA-256");
111106
        } catch (NoSuchAlgorithmException e) {
112107
            e.printStackTrace();
113108
        }
109+
        if(digest == null)
110+
            return null;
111+
114112
        digest.reset();
115113
        try {
116-
            byte data[] = new byte[4096];
114+
            byte[] data = new byte[4096];
117115
            FileInputStream fr = new FileInputStream(file);
118116
            int count;
119117
            while((count = fr.read(data)) != -1) {

app/src/main/java/eu/lepiller/nani/dictionary/DictionaryFactory.java

33
import android.content.Context;
44
import android.os.Build;
55
import android.os.LocaleList;
6-
import android.util.ArrayMap;
76
import android.util.Log;
87
98
import java.io.BufferedReader;
109
import java.io.File;
11-
import java.io.FileInputStream;
1210
import java.io.FileNotFoundException;
1311
import java.io.FileReader;
1412
import java.io.IOException;

2018
import java.util.Locale;
2119
import java.util.Map;
2220
23-
import eu.lepiller.nani.R;
2421
import eu.lepiller.nani.result.Result;
2522
2623
public class DictionaryFactory {

6057
6158
    private static String chooseLanguage(Map<String, StringBuilder> data) {
6259
        for(String l: languages) {
63-
            if(data.containsKey(l))
64-
                return data.get(l).toString();
60+
            if(data.containsKey(l)) {
61+
                StringBuilder sb = data.get(l);
62+
                if(sb != null)
63+
                    return sb.toString();
64+
            }
6565
        }
6666
        return null;
6767
    }

app/src/main/java/eu/lepiller/nani/dictionary/FileDictionary.java

1212
import java.util.Map;
1313
1414
public abstract class FileDictionary extends Dictionary {
15+
    String encoding = "UTF-8";
16+
1517
    interface Huffman {
1618
    }
1719

4749
    public void switchToCacheFile() {
4850
        if(checkTemporaryFile()) {
4951
            if(getFile().exists()) {
50-
                getFile().delete();
52+
                if(!getFile().delete())
53+
                    Log.w(TAG, getFile() + " was not deleted as expected.");
5154
            }
52-
            getTemporaryFile().renameTo(getFile());
55+
56+
            if(!getTemporaryFile().renameTo(getFile()))
57+
                Log.w(TAG, getTemporaryFile() + " was not renamed to " + getFile() + " as expected.");
5358
        }
5459
    }
5560

5964
            File sha256 = new File(getFile() + ".sha256");
6065
            if(sha256.exists()) {
6166
                String hash = sha256OfFile(f);
67+
                if(hash == null)
68+
                    return false;
6269
6370
                try {
6471
                    String expected = readSha256FromFile(sha256);

102109
    }
103110
104111
    @Override
105-
    int currentSize() {
106-
        return 0;
107-
    }
108-
109-
    @Override
110112
    public Map<String, Pair<File, File>> getDownloads() {
111113
        HashMap<String, Pair<File, File>> result = new HashMap<>();
112114
        Pair<File, File> pair = new Pair<>(getTemporaryFile(), getFile());

117119
    @Override
118120
    public void remove() {
119121
        if(getFile().exists())
120-
            getFile().delete();
122+
            if(!getFile().delete())
123+
                Log.w(TAG, getFile() + " was not deleted as expected.");
121124
        if(getTemporaryFile().exists())
122-
            getTemporaryFile().delete();
125+
            if(!getTemporaryFile().delete())
126+
                Log.w(TAG, getTemporaryFile() + " was not deleted as expected.");
123127
    }
124128
125129

142146
        for(int j=0; j<bs.size(); j++) {
143147
            str[j] = bs.get(j);
144148
        }
145-
        return new String(str, "UTF-8");
149+
        return new String(str, encoding);
146150
    }
147151
148152
    ArrayList<String> getStringList(RandomAccessFile file) throws IOException {

158162
        ArrayList<Integer> results = new ArrayList<>();
159163
        int number = file.readShort();
160164
        for(int i=0; i<number; i++) {
161-
            results.add(Integer.valueOf(file.readByte()));
165+
            results.add((int) file.readByte());
162166
        }
163167
        return results;
164168
    }

183187
            for(int i=0; i<bs.size(); i++) {
184188
                array[i] = bs.get(i);
185189
            }
186-
            return new HuffmanValue(new String(array, "UTF-8"));
190+
            return new HuffmanValue(new String(array, encoding));
187191
        }
188192
    }
189193
190-
    String getHuffmanString(RandomAccessFile file, Huffman huffman) throws IOException {
194+
    private String getHuffmanString(RandomAccessFile file, Huffman huffman) throws IOException {
191195
        StringBuilder b = new StringBuilder();
192196
        ArrayList<Boolean> bits = new ArrayList<>();
193197
        String c = null;

app/src/main/java/eu/lepiller/nani/dictionary/IncompatibleFormatException.java

11
package eu.lepiller.nani.dictionary;
22
33
public class IncompatibleFormatException extends DictionaryException {
4-
    String name;
4+
    private String name;
55
6-
    public IncompatibleFormatException(String name) {
6+
    IncompatibleFormatException(String name) {
77
        this.name = name;
88
    }
99

app/src/main/java/eu/lepiller/nani/dictionary/JMDict.java

66
import java.io.FileNotFoundException;
77
import java.io.IOException;
88
import java.io.RandomAccessFile;
9-
import java.net.MalformedURLException;
10-
import java.net.URL;
119
import java.util.ArrayList;
1210
import java.util.Arrays;
13-
import java.util.HashMap;
14-
import java.util.Map;
1511
1612
import eu.lepiller.nani.R;
1713
import eu.lepiller.nani.result.Result;

111107
112108
    private ArrayList<Integer> searchTrie(RandomAccessFile file, long triePos, byte[] txt) throws IOException {
113109
        Log.d(TAG, "searchTrie: " + triePos);
114-
        Log.d(TAG, "Remaining transitions: " + new String(txt, "UTF-8"));
110+
        Log.d(TAG, "Remaining transitions: " + new String(txt, encoding));
115111
        file.seek(triePos);
116112
        Log.d(TAG, "pointer: " + file.getFilePointer());
117113
        if(txt.length == 0) {