package com.madeinhk.english_chinesedictionary.service; import android.app.Notification; import android.app.NotificationManager; import android.app.Service; import android.content.ClipData; import android.content.ClipDescription; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; import android.os.IBinder; import android.support.v4.app.RemoteInput; import android.support.v4.os.BuildCompat; import android.support.v7.preference.PreferenceManager; import android.text.TextUtils; import android.util.Log; import com.madeinhk.utils.QuickLookupNotificationHelper; import com.madeinhk.english_chinesedictionary.SettingFragment; import com.madeinhk.model.ECDictionary; import com.madeinhk.model.Word; import com.madeinhk.utils.Stemmer; import com.madeinhk.utils.StringUtils; import org.jsoup.Jsoup; public class ECDictionaryService extends Service { private static final String TAG = "ECDictionaryService"; private boolean mRegistered = false; private String mLastWord; private long mTimestamp; private static int NOTIFICATION_ID = 1; public static final String KEY_TEXT_REPLY = "key_text_reply"; public static final String ACTION_QUICK_LOOKUP = "quick_lookup"; public static final String KEY_IS_FOREGROUND = "key_is_foreground"; public static final String ACTION_CHANGE_FOREGROUND = "change_foreground"; private QuickLookupNotificationHelper mQuickLookHelper; public ECDictionaryService() { } public void onCreate() { Log.d(TAG, "onCreate: " + mRegistered); mQuickLookHelper = new QuickLookupNotificationHelper(this); if (isQuickLookupEnabled()) { startForeground(); } synchronized (mClipListener) { if (!mRegistered) { ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); clipboardManager.addPrimaryClipChangedListener(mClipListener); mRegistered = true; } } } private void startForeground() { Notification notification = mQuickLookHelper.buildInitialNotification(); startForeground(NOTIFICATION_ID, notification); } public static void start(Context context) { context.startService( new Intent(context.getApplicationContext(), ECDictionaryService.class)); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent != null) { if (isQuickLookupEnabled() && ACTION_QUICK_LOOKUP.equals(intent.getAction())) { String text = getMessageText(intent); if (text != null) { text = text.trim(); } Word word = lookupWord(text); Notification notification = mQuickLookHelper.buildNotificationForResult(word); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.notify(NOTIFICATION_ID, notification); } else if (ACTION_CHANGE_FOREGROUND.equals(intent.getAction())) { boolean isForeground = intent.getBooleanExtra(KEY_IS_FOREGROUND, true); if (isForeground) { startForeground(); } else { stopForeground(true); } } } return START_STICKY; } @Override public IBinder onBind(Intent intent) { throw null; } ClipboardManager.OnPrimaryClipChangedListener mClipListener = new ClipboardManager.OnPrimaryClipChangedListener() { @Override public void onPrimaryClipChanged() { if (!isCopyToLookupEnabled()) { return; } final ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); ClipData data = clipboard.getPrimaryClip(); if (data == null) { return; } String text = extractTextFromClipData(data); if (text != null) { text = text.trim(); } if (TextUtils.isEmpty(text) || isDuplicated(text)) { return; } mLastWord = text; mTimestamp = System.currentTimeMillis(); Word word = lookupWord(text); if (word != null) { DictionaryHeadService.show(ECDictionaryService.this, word); } } // To tackle with the funny behaviour of clipboard listener private boolean isDuplicated(String text) { return text.equals(mLastWord) && System.currentTimeMillis() - mTimestamp < 1000; } // It seems that the clipboard api is quite buggy, defensive coding here private String extractTextFromClipData(ClipData clipData) { if (clipData == null) { return null; } ClipData.Item item = clipData.getItemAt(0); if (item == null) { return null; } ClipDescription description = clipData.getDescription(); if (description == null) { return null; } String mimeType = description.getMimeType(0); if ("text/plain".equals(mimeType)) { CharSequence text = item.getText(); if (text != null) { return text.toString(); } } else if ("text/html".equals(mimeType) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { String html = item.getHtmlText(); return Jsoup.parse(html).text(); } return null; } }; @Override public void onDestroy() { super.onDestroy(); if (mRegistered) { ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); clipboardManager.removePrimaryClipChangedListener(mClipListener); } } private Word lookupWord(String text) { if (TextUtils.isEmpty(text)) { return null; } ECDictionary dictionary = new ECDictionary(ECDictionaryService.this); String str = text.toLowerCase(); Word word = dictionary.lookup(str); if (word != null) { return word; } if (StringUtils.isEnglishWord(str)) { // Try to have stemming Stemmer stemmer = new Stemmer(); stemmer.add(str.toCharArray(), str.length()); stemmer.stem(); return dictionary.lookup(stemmer.toString()); } return null; } private boolean isCopyToLookupEnabled() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); return preferences.getBoolean(SettingFragment.KEY_COPY_TO_LOOKUP, true); } private boolean isQuickLookupEnabled() { SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); return BuildCompat.isAtLeastN() && preferences.getBoolean(SettingFragment.KEY_QUICK_LOOKUP, true); } private String getMessageText(Intent intent) { Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); if (remoteInput != null) { return remoteInput.getCharSequence(KEY_TEXT_REPLY).toString(); } return null; } public static Intent getQuickLookupIntent(Context context) { return new Intent(context, ECDictionaryService.class).setAction(ACTION_QUICK_LOOKUP); } public static Intent getChangeForegroundIntent(Context context, boolean isForeground) { Intent intent = new Intent(context, ECDictionaryService.class). setAction(ACTION_CHANGE_FOREGROUND); intent.putExtra(KEY_IS_FOREGROUND, isForeground); return intent; } }