diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 057100a6f..c775e98c1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,16 +14,16 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> + - + + + - - - - - + - @@ -69,7 +68,6 @@ - + + + + + + + + + + + + + + + @@ -130,44 +152,44 @@ - + android:theme="@style/AppTheme" /> - + android:label="@string/excluded_languages" + android:theme="@style/AppTheme.NoActionBar" /> - + android:label="@string/dir_picker" + android:theme="@style/AppTheme.NoActionBar" /> - + android:label="@string/license" + android:theme="@style/AppTheme.NoActionBar" /> - + android:label="@string/filter" + android:theme="@style/AppTheme.NoActionBar" /> + android:label="@string/set_pattern_protection" + android:theme="@style/AppTheme.NoActionBar" /> - + android:label="@string/download_service_label" /> + + + diff --git a/app/src/main/java/com/hippo/dict/DictDatabase.java b/app/src/main/java/com/hippo/dict/DictDatabase.java new file mode 100644 index 000000000..bf9d7f943 --- /dev/null +++ b/app/src/main/java/com/hippo/dict/DictDatabase.java @@ -0,0 +1,255 @@ +package com.hippo.dict; + +/* + * Copyright (C) 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.net.Uri; +import android.text.TextUtils; +import android.util.JsonReader; + +import com.hippo.dict.util.DictLog; +import com.hippo.util.SqlUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.Set; + + +public class DictDatabase { + + private static final String TAG = DictDatabase.class.getSimpleName(); + + public static final String COLUMN_PARENT = "parent"; + public static final String COLUMN_DATA = "data"; + public static final String COLUMN_DICT = "dict"; + + private static final String DATABASE_NAME = "dict_database.db"; + private static final String TABLE_DICT = "dict"; + private static final String SEPARATOR = "@@@"; + private SQLiteDatabase mDatabase; + private static DictDatabase sInstance; + + private String mDictName; + private Integer mItemNum; + private boolean mAbortFlag = false; + + public static DictDatabase getInstance(Context context) { + if (sInstance == null) { + sInstance = new DictDatabase(context.getApplicationContext()); + } + return sInstance; + } + + private DictDatabase(Context context) { + DatabaseHelper databaseHelper = new DatabaseHelper(context); + mDatabase = databaseHelper.getWritableDatabase(); + } + + public String[] getEnSuggestions(String prefix) { + // TODO add limit + if (TextUtils.isEmpty(prefix)) { + return new String[0]; + } + + StringBuilder sb = new StringBuilder(); + sb.append("SELECT * FROM ").append(TABLE_DICT); + sb.append(" WHERE ").append(COLUMN_DATA).append(" LIKE '") + .append("%").append(SEPARATOR) + .append(SqlUtils.sqlEscapeString(prefix)) + .append(SEPARATOR).append("%'") + .append(" LIMIT 5"); + return suggestionsQuery(sb.toString()); + } + + public String[] getKeywordSuggestions(String prefix) { + // TODO add limit + if (TextUtils.isEmpty(prefix)) { + return new String[0]; + } + + StringBuilder sb = new StringBuilder(); + sb.append("SELECT * FROM ").append(TABLE_DICT); + sb.append(" WHERE ").append(COLUMN_DATA).append(" LIKE '") + .append("%") + .append(SqlUtils.sqlEscapeString(prefix)) + .append("%'").append(" LIMIT 15"); + String result[] = suggestionsQuery(sb.toString()); + Set tmp = new HashSet<>(); + for (String s : result) { + if (s.contains(prefix)) { + tmp.add(s); + } + } + return tmp.toArray(new String[tmp.size()]); + } + + public String[] getPrefixSuggestions(String prefix) { + String result[] = getKeywordSuggestions(prefix); + Set tmp = new HashSet<>(); + for (String s : result) { + if (s.startsWith(prefix)) { + tmp.add(s); + } + } + return tmp.toArray(new String[tmp.size()]); + } + + private String[] suggestionsQuery(String sql) { + Set queryList = new HashSet<>(); + Cursor cursor = mDatabase.rawQuery(sql, null); + + int queryIndex = cursor.getColumnIndex(COLUMN_DATA); + if (cursor.moveToFirst()) { + while (!cursor.isAfterLast()) { + String data = cursor.getString(queryIndex); + String datas[] = data.split(SEPARATOR); + for (String item : datas) { + if (TextUtils.isEmpty(item)) { + continue; + } + queryList.add(item); + } + cursor.moveToNext(); + } + } + cursor.close(); + return queryList.toArray(new String[queryList.size()]); + } + + public void importDict(final Uri dictUri, final DictImportService.ProcessListener listener) throws IOException, URISyntaxException { + mAbortFlag = false; + File dictFile = new File(dictUri.getPath()); + FileInputStream fileInputStream = new FileInputStream(dictFile); + JsonReader jsonReader = new JsonReader(new InputStreamReader( + fileInputStream, "UTF-8")); + + jsonReader.beginObject(); + while (jsonReader.hasNext()) { + String field = jsonReader.nextName(); + if (field.equals("dict")) { + mDictName = jsonReader.nextString(); + DictLog.t(TAG, "[importDict] prase the dict name -- " + mDictName); + } else if (field.equals("data")) { + deletDict(mDictName); + praseData(jsonReader, listener); + + } else if (field.equals("num")) { + mItemNum = jsonReader.nextInt(); + listener.processTotal(mItemNum); + DictLog.t(TAG, "[importDict] prase the item number -- " + mItemNum); + } + } + jsonReader.endObject(); + jsonReader.close(); + listener.processComplete(); + } + + public void addItem(String data, String parent, String dict) { + ContentValues values = new ContentValues(); + values.put(COLUMN_DATA, data); + values.put(COLUMN_PARENT, parent); + values.put(COLUMN_DICT, dict); + mDatabase.insert(TABLE_DICT, null, values); + } + + public void deletDict(String dict) { + DictLog.t(TAG, "[deletDict] dict:" + dict); + mDatabase.delete(TABLE_DICT, COLUMN_DICT + "=?", new String[]{dict}); + } + + public void importAbort() { + DictLog.t(TAG, "[importAbort] set mAbortFlag true"); + mAbortFlag = true; + } + + /** + * Builds the database. This version has extra support for using the version field + * as a mode flags field, and configures the database columns depending on the mode bits + * (features) requested by the extending class. + */ + private static class DatabaseHelper extends SQLiteOpenHelper { + + public DatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, 1); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE " + TABLE_DICT + " (" + + "_id INTEGER PRIMARY KEY" + + "," + COLUMN_DATA + " TEXT" + + "," + COLUMN_PARENT + " TEXT" + + "," + COLUMN_DICT + " TEXT" + + ");"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.execSQL("DROP TABLE IF EXISTS " + TABLE_DICT); + onCreate(db); + } + } + + private void praseData(JsonReader jsonReader, final DictImportService.ProcessListener listener) throws IOException { + int process = 1; + jsonReader.beginArray(); + while (jsonReader.hasNext()) { + synchronized (this) { + if (mAbortFlag) { + DictLog.t(TAG, "[praseData] import abort,delect the dict -- " + mDictName); + deletDict(mDictName); + return; + } + } + + praseSingleData(jsonReader); + listener.process(process); + process++; + } + jsonReader.endArray(); + } + + private void praseSingleData(JsonReader jsonReader) throws IOException { + StringBuilder sb = new StringBuilder(); + String parent = ""; + sb.append(SEPARATOR); + jsonReader.beginObject(); + while (jsonReader.hasNext()) { + String name = jsonReader.nextName(); + String content = jsonReader.nextString(); + if (name.equals("parent")) { + parent = content; + } else { + sb.append(content); + sb.append(SEPARATOR); + } + } + jsonReader.endObject(); + + DictLog.t(TAG, "[praseSingleData] item:" + parent + " " + sb.toString()); + addItem(sb.toString(), parent, mDictName); + } + +} diff --git a/app/src/main/java/com/hippo/dict/DictFilter.java b/app/src/main/java/com/hippo/dict/DictFilter.java new file mode 100644 index 000000000..3148876d7 --- /dev/null +++ b/app/src/main/java/com/hippo/dict/DictFilter.java @@ -0,0 +1,92 @@ +/* + * Copyright 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.dict; + +import android.content.Context; + +import com.hippo.dict.util.DictLog; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class DictFilter { + private static final String TAG = "DictFilter"; + + // pattern for language + private static final String enRegEx = "^[0-9a-zA-Z_\\s]+$"; + private static final String zhRegEx = "^[0-9a-zA-Z_\\s[\u4e00-\u9fa5]+]+$"; + + public static class EnFilter implements Filter { + + @Override + public String[] filter(String[] data) { + DictLog.t(TAG, "[enfilter] ------------------"); + Pattern localeRegEx = Pattern.compile(enRegEx); + List result = new ArrayList<>(); + for (String s : data) { + Matcher m = localeRegEx.matcher(s); + if (m.matches()) { + result.add(s); + DictLog.t(TAG, "[enfilter] " + s); + } + } + return result.toArray(new String[result.size()]); + } + } + + public static class LocaleFilter implements Filter { + + Context context; + + public LocaleFilter(Context context) { + this.context = context; + } + + @Override + public String[] filter(String[] data) { + DictLog.t(TAG, "[localefilter] ------------------"); + String localeString = context.getResources().getConfiguration().locale.getCountry(); + String localeRegEx; + + DictLog.t(TAG, "[localefilter] get locale:" + localeString); + + //TODO add more + if (localeString.equals("CN") || localeString.equals("TW")) { + localeRegEx = zhRegEx; + } else { + localeRegEx = enRegEx; + } + + Pattern localePattern = Pattern.compile(localeRegEx); + List result = new ArrayList<>(); + for (String s : data) { + Matcher m = localePattern.matcher(s); + if (m.matches()) { + result.add(s); + DictLog.t(TAG, "[localefilter] " + s); + } + } + return result.toArray(new String[result.size()]); + } + } + + public interface Filter { + String[] filter(String[] data); + } +} diff --git a/app/src/main/java/com/hippo/dict/DictImportService.java b/app/src/main/java/com/hippo/dict/DictImportService.java new file mode 100644 index 000000000..48e04f4f3 --- /dev/null +++ b/app/src/main/java/com/hippo/dict/DictImportService.java @@ -0,0 +1,218 @@ +/* + * Copyright 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.hippo.dict; + +import android.app.Service; +import android.content.Intent; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Binder; +import android.os.IBinder; +import android.util.Log; + +import com.hippo.dict.util.DictLog; +import com.hippo.util.TextUrl; + +import java.util.ArrayList; +import java.util.List; + +public class DictImportService extends Service { + private static final String TAG = "DictImportSerevice"; + + private DictManager mDictManager; + private List mListeners = new ArrayList<>(); + private ProcessListener mDictProcessListener; + private AsyncTask mImportAsyncTask; + private DictNotification mDictNotification; + + // current task information + private int mItemNum; + private Uri mDictUri; + private boolean mRunningFlag = false; + + public DictImportService() { + + } + + private Binder mServiceBinder = new DictImportServiceBinder(); + + @Override + public IBinder onBind(Intent intent) { + return mServiceBinder; + } + + @Override + public void onCreate() { + super.onCreate(); + mDictManager = new DictManager(this); + mDictNotification = new DictNotification(this); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + + return super.onStartCommand(intent, flags, startId); + } + + public interface ProcessListener { + void process(int progress); + + void processTotal(int total); + + void processComplete(); + } + + public class DictImportServiceBinder extends Binder { + public DictImportService getService() { + return DictImportService.this; + } + } + + public void importDict(Uri dictUri) { + if (dictUri == null) { + // TODO error tip + Log.e(TAG, "[importDict] dictUri is null"); + return; + } + + DictLog.t(TAG, "[importDict] start import async task"); + mImportAsyncTask = new ImportAsyncTask(dictUri).execute(); + } + + public void abortImport() { + if (mImportAsyncTask == null) { + Log.e(TAG, "[abortImport] mImportAsyncTask is null"); + return; + } + + DictLog.t(TAG, "[abortImport] improt abort"); + + // fixme + // this i just set a flag to abort the prase thread,it may cause a exception + // it there may be a elegant way to shut the worker thread down + mDictManager.importAbort(); + + // just for service go die + mImportAsyncTask.cancel(true); + } + + public Uri getUri() { + return mDictUri; + } + + public int getItemNum() { + return mItemNum; + } + + public boolean isRunning() { + return mRunningFlag; + } + + public void setOnProgressListener(ProcessListener onProgressListener) { + if (onProgressListener != null) { + mListeners.add(onProgressListener); + mListeners.remove(mDictNotification.mNotificationListener); + mDictNotification.stopNotify(); + } + } + + public void removeOnProgressListener(ProcessListener onProgressListener) { + if (onProgressListener != null) { + mListeners.remove(onProgressListener); + } + + // if there is no listener in listener list,we is in the backgroud mostly + // we post the process progress informantion to a notification + if (mListeners.size() == 0) { + mDictNotification.setMax(mItemNum); + mDictNotification.setFileName(TextUrl.getFileName(mDictUri.toString())); + mListeners.add(mDictNotification.mNotificationListener); + + } + } + + class ImportAsyncTask extends AsyncTask { + + public ImportAsyncTask(Uri dictUri) { + mDictUri = dictUri; + mRunningFlag = true; + mDictProcessListener = new ProcessListener() { + @Override + public void process(int progress) { + DictLog.t(TAG, "[process] progress:" + progress); + publishProgress(progress); + } + + @Override + public void processTotal(int total) { + DictLog.t(TAG, "process total " + total); + mItemNum = total; + for (ProcessListener listener : mListeners) { + listener.processTotal(total); + } + } + + @Override + public void processComplete() { + // we don't do any thing here,let async task handle this + DictLog.t(TAG, "[processComplete] do nothing"); + } + + }; + } + + @Override + protected Void doInBackground(Void... voids) { + try { + mDictManager.importDict(mDictUri, mDictProcessListener); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + protected void onProgressUpdate(Integer... progress) { + super.onProgressUpdate(progress); + + DictLog.t(TAG, "[onProgressUpdate] process item " + progress[0]); + for (ProcessListener listener : mListeners) { + listener.process(progress[0]); + } + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + for (ProcessListener listener : mListeners) { + listener.processComplete(); + } + mRunningFlag = false; + DictImportService.this.godie(); + } + + @Override + protected void onCancelled() { + super.onCancelled(); + DictImportService.this.godie(); + } + } + + public void godie() { + DictLog.t(TAG, "[godie] task done or abort,go to die"); + this.stopSelf(); + } +} diff --git a/app/src/main/java/com/hippo/dict/DictManager.java b/app/src/main/java/com/hippo/dict/DictManager.java new file mode 100644 index 000000000..f727ff47a --- /dev/null +++ b/app/src/main/java/com/hippo/dict/DictManager.java @@ -0,0 +1,173 @@ +/* + * Copyright 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.hippo.dict; + +import android.content.Context; +import android.net.Uri; +import android.os.AsyncTask; +import android.util.Log; + +import com.hippo.dict.util.DictLog; + +import java.io.IOException; +import java.net.URISyntaxException; + +public class DictManager { + + private static final String TAG = "DictManager"; + private DictDatabase mDictDatabase; + private Context mContext; + private static boolean mEnFlag = true; + private static boolean mKeywordFlag = true; + private static boolean mPrefixFlag = false; + + private DictQueryAsyncTask mEnSuggestionsAsyncTask; + private DictQueryAsyncTask mKeywordQueryAsyncTask; + private DictQueryAsyncTask mPrefixQueryAsyncTask; + + public DictManager(Context context) { + mContext = context; + mDictDatabase = DictDatabase.getInstance(mContext); + } + + public void importDict(final Uri dictUri, final DictImportService.ProcessListener listener) + throws IOException, URISyntaxException { + mDictDatabase.importDict(dictUri, listener); + } + + public void deletDict(String dict) { + mDictDatabase.deletDict(dict); + } + + public void getEnSuggestions(String prefix, final OnDictQueryResultListener listener) { + if (!mEnFlag) { + Log.w(TAG, "get en suggestions is be disable"); + return; + } + + if (prefix == null) { + Log.e(TAG, "prefix should not be null"); + return; + } + + if (mEnSuggestionsAsyncTask != null) { + mEnSuggestionsAsyncTask.cancel(true); + } + + mEnSuggestionsAsyncTask = new DictQueryAsyncTask(prefix, listener, new OpPolicy() { + @Override + public String[] getSuggestions(String prefix) { + return mDictDatabase.getEnSuggestions(prefix); + } + }, new DictFilter.EnFilter()); + mEnSuggestionsAsyncTask.execute(); + } + + public void getKeywordSuggestions(String prefix, final OnDictQueryResultListener listener) { + if (!mKeywordFlag) { + Log.w(TAG, "get keyword suggestions is be disable"); + return; + } + + if (prefix == null) { + Log.e(TAG, "prefix should not be null"); + return; + } + + if (mKeywordQueryAsyncTask != null) { + mKeywordQueryAsyncTask.cancel(true); + } + + mKeywordQueryAsyncTask = new DictQueryAsyncTask(prefix, listener, new OpPolicy() { + @Override + public String[] getSuggestions(String prefix) { + return mDictDatabase.getKeywordSuggestions(prefix); + } + }, new DictFilter.LocaleFilter(mContext)); + mKeywordQueryAsyncTask.execute(); + } + + public void getPrefixSuggestions(String prefix, final OnDictQueryResultListener listener) { + if (!mPrefixFlag) { + Log.w(TAG, "get prefix suggestions is be disable"); + return; + } + + if (prefix == null) { + Log.e(TAG, "prefix should not be null"); + return; + } + + if (mPrefixQueryAsyncTask != null) { + mPrefixQueryAsyncTask.cancel(true); + } + + mPrefixQueryAsyncTask = new DictQueryAsyncTask(prefix, listener, new OpPolicy() { + @Override + public String[] getSuggestions(String prefix) { + return mDictDatabase.getPrefixSuggestions(prefix); + } + }, new DictFilter.LocaleFilter(mContext)); + mPrefixQueryAsyncTask.execute(); + } + + public void importAbort() { + mDictDatabase.importAbort(); + } + + private class DictQueryAsyncTask extends AsyncTask { + private String prefix; + private OnDictQueryResultListener listener; + private OpPolicy op; + private DictFilter.Filter filter; + + public DictQueryAsyncTask(String prefix, final OnDictQueryResultListener listener, + OpPolicy op, DictFilter.Filter filter) { + this.prefix = prefix; + this.listener = listener; + this.op = op; + this.filter = filter; + } + + @Override + protected String[] doInBackground(Void... voids) { + return filter.filter(op.getSuggestions(prefix)); + } + + @Override + protected void onPostExecute(String[] result) { + super.onPostExecute(result); + listener.getResult(result); + } + + @Override + protected void onCancelled() { + super.onCancelled(); + DictLog.t(TAG, "[onCancelled] prefix query task for prefix " + prefix + " abort"); + } + } + + public interface OnDictQueryResultListener { + void getResult(String[] result); + } + + // the operation of querying of database + // used in DictQueryAsyncTask + private interface OpPolicy { + String[] getSuggestions(String prefix); + } +} diff --git a/app/src/main/java/com/hippo/dict/DictNotification.java b/app/src/main/java/com/hippo/dict/DictNotification.java new file mode 100644 index 000000000..a2dc7a40f --- /dev/null +++ b/app/src/main/java/com/hippo/dict/DictNotification.java @@ -0,0 +1,99 @@ +/* + * Copyright 2015 Hippo Seven + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.hippo.dict; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.support.v7.app.NotificationCompat; + +import com.hippo.ehviewer.R; +import com.hippo.ehviewer.ui.DictImportActivity; +import com.hippo.ehviewer.ui.MainActivity; + + +public class DictNotification { + + private Context mContext; + private NotificationManager mNotifyManager; + private NotificationCompat.Builder mBuilder; + private int mMax = 100; + private int mId = 1; + + private PendingIntent mImportIntent; + private PendingIntent mDoneIntent; + + public DictNotification(Context context) { + + mContext = context; + mNotifyManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + mBuilder = new NotificationCompat.Builder(mContext); + + mImportIntent = PendingIntent.getActivity(context, 0, new Intent(context, DictImportActivity.class), 0); + mDoneIntent = PendingIntent.getActivity(context, 0, new Intent(context, MainActivity.class), 0); + + mBuilder.setContentTitle(mContext.getResources().getString(R.string.dict_import)) + .setContentText("0/" + mMax) + .setSmallIcon(R.mipmap.ic_launcher) + .setContentIntent(mImportIntent) + .setOngoing(true); + } + + public DictImportService.ProcessListener mNotificationListener = new DictImportService.ProcessListener() { + @Override + public void process(int progress) { + DictNotification.this.notify(progress); + } + + @Override + public void processTotal(int total) { + // this may useless + mMax = total; + } + + @Override + public void processComplete() { + DictNotification.this.notifyDone(); + } + }; + + public void setMax(int max) { + mMax = max; + mBuilder.setContentText("0/" + mMax); + } + + public void setFileName(String fileName) { + mBuilder.setContentTitle(mContext.getResources().getString(R.string.dict_import) + " " + fileName); + } + + public void notify(int progress) { + mBuilder.setContentIntent(mImportIntent); + mBuilder.setProgress(mMax, progress, false); + mBuilder.setContentText(progress + "/" + mMax); + mNotifyManager.notify(mId, mBuilder.build()); + } + + public void notifyDone() { + mBuilder.setContentIntent(mDoneIntent); + mBuilder.setOngoing(false); + mNotifyManager.notify(mId, mBuilder.build()); + } + + public void stopNotify() { + mNotifyManager.cancel(mId); + } +} diff --git a/app/src/main/java/com/hippo/dict/util/DictLog.java b/app/src/main/java/com/hippo/dict/util/DictLog.java new file mode 100644 index 000000000..9f913e9bf --- /dev/null +++ b/app/src/main/java/com/hippo/dict/util/DictLog.java @@ -0,0 +1,16 @@ +package com.hippo.dict.util; + +import android.util.Log; + +/** + * Created by axlecho on 2016/4/8. + */ +public class DictLog { + + static private boolean isTrace = true; + + static public void t(String tag, String msg) { + if (!isTrace) return; + Log.d(tag, msg); + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/DictImportActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/DictImportActivity.java new file mode 100644 index 000000000..f91cb9bfb --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/DictImportActivity.java @@ -0,0 +1,188 @@ +package com.hippo.ehviewer.ui; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.net.Uri; +import android.os.Bundle; +import android.os.IBinder; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.hippo.dict.DictImportService; +import com.hippo.ehviewer.R; +import com.hippo.util.TextUrl; + +public class DictImportActivity extends EhActivity { + + private final static String TAG = "DictImportActivity"; + private DictImportService serviceBinder; + private int mItemNum = 0; + + private Button mConfirmBtn; + private Button mCancelBtn; + private Button mHideBtn; + + private ProgressBar mProgressBar; + private TextView mProgressTipView; + private TextView mTipView; + private Uri mDictUri; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_dict_improt); + + Intent intent = new Intent(DictImportActivity.this, DictImportService.class); + Log.d(TAG, "[onCreate] bind service"); + bindService(intent, conn, Context.BIND_AUTO_CREATE); + startService(intent); + + + mConfirmBtn = (Button) findViewById(R.id.btn_confirm); + mCancelBtn = (Button) findViewById(R.id.btn_cancel); + mHideBtn = (Button) findViewById(R.id.btn_hide); + + mProgressBar = (ProgressBar) findViewById(R.id.bar_import); + mProgressTipView = (TextView) findViewById(R.id.tv_progress); + mTipView = (TextView) findViewById(R.id.tv_tip); + + mConfirmBtn.setOnClickListener(confirmListener); + mCancelBtn.setOnClickListener(cancelListener); + mHideBtn.setOnClickListener(hideListener); + } + + private ServiceConnection conn = new ServiceConnection() { + @Override + public void onServiceDisconnected(ComponentName name) { + Log.w(TAG, "[onServiceConnected] disconnect to service"); + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + serviceBinder = ((DictImportService.DictImportServiceBinder) service).getService(); + serviceBinder.setOnProgressListener(importListener); + + // if we are doing a task,we load improt infomation from service + // and ignore the newer import task + if (serviceBinder.isRunning()) { + initFromSerivce(); + } else { + initFromIntent(); + } + + Log.d(TAG, "[onServiceConnected] connect to service"); + } + }; + + private DictImportService.ProcessListener importListener = new DictImportService.ProcessListener() { + @Override + public void process(int progress) { + Log.i(TAG, "[process] " + progress); + mProgressBar.setProgress(progress); + mProgressTipView.setText(progress + "/" + mItemNum); + } + + @Override + public void processTotal(int total) { + mItemNum = total; + mProgressBar.setMax(mItemNum); + } + + @Override + public void processComplete() { + mCancelBtn.setText(getResources().getString(R.string.dict_done)); + mHideBtn.setVisibility(View.GONE); + } + }; + + + private View.OnClickListener cancelListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + DictImportActivity.this.finish(); + } + }; + + private View.OnClickListener abortListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + + Log.d(TAG, "[onClick] abort"); + if (serviceBinder != null) { + serviceBinder.abortImport(); + } + DictImportActivity.this.finish(); + } + }; + + private View.OnClickListener confirmListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + Log.d(TAG, "[onClick] confirm"); + if (serviceBinder == null) { + // todo error tip + Log.e(TAG, "[onClick] service is not connected"); + return; + } + if (mDictUri == null) { + // todo error tip + Log.e(TAG, "[onClick] mDictUri is not be init"); + return; + } + serviceBinder.importDict(mDictUri); + + view.setVisibility(View.GONE); + mCancelBtn.setOnClickListener(abortListener); + mHideBtn.setVisibility(View.VISIBLE); + mProgressBar.setVisibility(View.VISIBLE); + + } + }; + + private View.OnClickListener hideListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + Log.d(TAG, "[onClick] hide"); + DictImportActivity.this.finish(); + } + }; + + @Override + protected void onDestroy() { + serviceBinder.removeOnProgressListener(importListener); + unbindService(conn); + super.onDestroy(); + } + + + private void initFromSerivce() { + mDictUri = serviceBinder.getUri(); + if (mDictUri == null) { + Log.e(TAG, "[initFromSerivce] error use of initFromeService,you may use initFromIntent instead"); + return; + } + + Log.d(TAG, "[initFromSerivce] " + mDictUri.toString()); + + mItemNum = serviceBinder.getItemNum(); + mProgressBar.setMax(mItemNum); + mConfirmBtn.setVisibility(View.GONE); + mHideBtn.setVisibility(View.VISIBLE); + mProgressBar.setVisibility(View.VISIBLE); + mTipView.setText(TextUrl.getFileName(mDictUri.toString())); + mCancelBtn.setOnClickListener(abortListener); + } + + private void initFromIntent() { + mDictUri = getIntent().getData(); + Log.d(TAG, "[initFromIntent] " + mDictUri.toString()); + mTipView.setText(TextUrl.getFileName(mDictUri.toString())); + } + +} diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java b/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java index d9d91f2ef..c26ed6a8f 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java @@ -43,6 +43,7 @@ import android.widget.ListView; import android.widget.TextView; +import com.hippo.dict.DictManager; import com.hippo.ehviewer.R; import com.hippo.view.ViewTransition; import com.hippo.yorozuya.AnimationUtils; @@ -52,6 +53,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; public class SearchBar extends FrameLayout implements View.OnClickListener, @@ -85,6 +87,7 @@ public class SearchBar extends FrameLayout implements View.OnClickListener, private ViewTransition mViewTransition; private SearchDatabase mSearchDatabase; + private DictManager mDictManager; private List mSuggestionList; private ArrayAdapter mSuggestionAdapter; @@ -114,6 +117,7 @@ private void init(Context context) { setBackgroundResource(R.drawable.card_white_no_padding_2dp); mSearchDatabase = SearchDatabase.getInstance(getContext()); + mDictManager = new DictManager(getContext()); LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(R.layout.widget_search_bar, this); @@ -169,7 +173,8 @@ private void removeListHeader() { } private void updateSuggestions() { - String prefix = mEditText.getText().toString(); + final String prefix = mEditText.getText().toString(); + String[] suggestions = mSearchDatabase.getSuggestions(prefix); mSuggestionList.clear(); Collections.addAll(mSuggestionList, suggestions); @@ -179,6 +184,23 @@ private void updateSuggestions() { addListHeader(); } mSuggestionAdapter.notifyDataSetChanged(); + + final DictManager.OnDictQueryResultListener listener = new DictManager.OnDictQueryResultListener() { + @Override + public void getResult(String[] result) { + HashSet set = new HashSet<>(); + set.addAll(mSuggestionList); + Collections.addAll(set, result); + mSuggestionList.clear(); + mSuggestionList.addAll(set); + mSuggestionList.remove(prefix); + Collections.sort(mSuggestionList); + mSuggestionAdapter.notifyDataSetChanged(); + } + }; + mDictManager.getEnSuggestions(prefix, listener); + mDictManager.getKeywordSuggestions(prefix, listener); + mDictManager.getPrefixSuggestions(prefix, listener); } public void setAllowEmptySearch(boolean allowEmptySearch) { @@ -479,10 +501,15 @@ public void onRestoreInstanceState(Parcelable state) { public interface Helper { void onClickTitle(); + void onClickLeftIcon(); + void onClickRightIcon(); + void onSearchEditTextClick(); + void onApplySearch(String query); + void onSearchEditTextBackPressed(); } diff --git a/app/src/main/java/com/hippo/util/TextUrl.java b/app/src/main/java/com/hippo/util/TextUrl.java index f9461dae9..e9b8fbd41 100644 --- a/app/src/main/java/com/hippo/util/TextUrl.java +++ b/app/src/main/java/com/hippo/util/TextUrl.java @@ -56,4 +56,13 @@ public static CharSequence handleTextUrl(CharSequence content) { return spannable == null ? content : spannable; } + + public static String getFileName(String path) { + if (path == null) { + return path; + } + + int start = path.lastIndexOf("/"); + return start == -1 ? path : path.substring(start + 1); + } } diff --git a/app/src/main/res/layout/activity_dict_improt.xml b/app/src/main/res/layout/activity_dict_improt.xml new file mode 100644 index 000000000..48230bcad --- /dev/null +++ b/app/src/main/res/layout/activity_dict_improt.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + +