| /** |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * 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.android.inputmethod.dictionarypack; |
| |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| import android.preference.Preference; |
| import android.util.Log; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.ViewParent; |
| import android.widget.ListView; |
| import android.widget.TextView; |
| |
| import com.android.inputmethod.latin.R; |
| |
| import java.util.Locale; |
| |
| /** |
| * A preference for one word list. |
| * |
| * This preference refers to a single word list, as available in the dictionary |
| * pack. Upon being pressed, it displays a menu to allow the user to install, disable, |
| * enable or delete it as appropriate for the current state of the word list. |
| */ |
| public final class WordListPreference extends Preference { |
| static final private String TAG = WordListPreference.class.getSimpleName(); |
| |
| // What to display in the "status" field when we receive unknown data as a status from |
| // the content provider. Empty string sounds sensible. |
| static final private String NO_STATUS_MESSAGE = ""; |
| |
| /// Actions |
| static final private int ACTION_UNKNOWN = 0; |
| static final private int ACTION_ENABLE_DICT = 1; |
| static final private int ACTION_DISABLE_DICT = 2; |
| static final private int ACTION_DELETE_DICT = 3; |
| |
| // Members |
| // The context to get resources |
| final Context mContext; |
| // The id of the client for which this preference is. |
| final String mClientId; |
| // The metadata word list id and version of this word list. |
| public final String mWordlistId; |
| public final int mVersion; |
| // The status |
| public int mStatus; |
| // The size of the dictionary file |
| private final int mFilesize; |
| |
| private final DictionaryListInterfaceState mInterfaceState; |
| private final OnWordListPreferenceClick mPreferenceClickHandler = |
| new OnWordListPreferenceClick(); |
| private final OnActionButtonClick mActionButtonClickHandler = |
| new OnActionButtonClick(); |
| |
| public WordListPreference(final Context context, |
| final DictionaryListInterfaceState dictionaryListInterfaceState, final String clientId, |
| final String wordlistId, final int version, final Locale locale, |
| final String description, final int status, final int filesize) { |
| super(context, null); |
| mContext = context; |
| mInterfaceState = dictionaryListInterfaceState; |
| mClientId = clientId; |
| mVersion = version; |
| mWordlistId = wordlistId; |
| mFilesize = filesize; |
| |
| setLayoutResource(R.layout.dictionary_line); |
| |
| setTitle(description); |
| setStatus(status); |
| setKey(wordlistId); |
| } |
| |
| private void setStatus(final int status) { |
| if (status == mStatus) return; |
| mStatus = status; |
| setSummary(getSummary(status)); |
| } |
| |
| private String getSummary(final int status) { |
| switch (status) { |
| // If we are deleting the word list, for the user it's like it's already deleted. |
| // It should be reinstallable. Exposing to the user the whole complexity of |
| // the delayed deletion process between the dictionary pack and Android Keyboard |
| // would only be confusing. |
| case MetadataDbHelper.STATUS_DELETING: |
| case MetadataDbHelper.STATUS_AVAILABLE: |
| return mContext.getString(R.string.dictionary_available); |
| case MetadataDbHelper.STATUS_DOWNLOADING: |
| return mContext.getString(R.string.dictionary_downloading); |
| case MetadataDbHelper.STATUS_INSTALLED: |
| return mContext.getString(R.string.dictionary_installed); |
| case MetadataDbHelper.STATUS_DISABLED: |
| return mContext.getString(R.string.dictionary_disabled); |
| default: |
| return NO_STATUS_MESSAGE; |
| } |
| } |
| |
| // The table below needs to be kept in sync with MetadataDbHelper.STATUS_* since it uses |
| // the values as indices. |
| private static final int sStatusActionList[][] = { |
| // MetadataDbHelper.STATUS_UNKNOWN |
| {}, |
| // MetadataDbHelper.STATUS_AVAILABLE |
| { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT }, |
| // MetadataDbHelper.STATUS_DOWNLOADING |
| { ButtonSwitcher.STATUS_CANCEL, ACTION_DISABLE_DICT }, |
| // MetadataDbHelper.STATUS_INSTALLED |
| { ButtonSwitcher.STATUS_DELETE, ACTION_DELETE_DICT }, |
| // MetadataDbHelper.STATUS_DISABLED |
| { ButtonSwitcher.STATUS_DELETE, ACTION_DELETE_DICT }, |
| // MetadataDbHelper.STATUS_DELETING |
| // We show 'install' because the file is supposed to be deleted. |
| // The user may reinstall it. |
| { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT } |
| }; |
| |
| private int getButtonSwitcherStatus(final int status) { |
| if (status >= sStatusActionList.length) { |
| Log.e(TAG, "Unknown status " + status); |
| return ButtonSwitcher.STATUS_NO_BUTTON; |
| } |
| return sStatusActionList[status][0]; |
| } |
| |
| private static int getActionIdFromStatusAndMenuEntry(final int status) { |
| if (status >= sStatusActionList.length) { |
| Log.e(TAG, "Unknown status " + status); |
| return ACTION_UNKNOWN; |
| } |
| return sStatusActionList[status][1]; |
| } |
| |
| private void disableDict() { |
| SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext); |
| CommonPreferences.disable(prefs, mWordlistId); |
| UpdateHandler.markAsUnused(mContext, mClientId, mWordlistId, mVersion, mStatus); |
| if (MetadataDbHelper.STATUS_DOWNLOADING == mStatus) { |
| setStatus(MetadataDbHelper.STATUS_AVAILABLE); |
| } else if (MetadataDbHelper.STATUS_INSTALLED == mStatus) { |
| // Interface-wise, we should no longer be able to come here. However, this is still |
| // the right thing to do if we do come here. |
| setStatus(MetadataDbHelper.STATUS_DISABLED); |
| } else { |
| Log.e(TAG, "Unexpected state of the word list for disabling " + mStatus); |
| } |
| } |
| private void enableDict() { |
| SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext); |
| CommonPreferences.enable(prefs, mWordlistId); |
| // Explicit enabling by the user : allow downloading on metered data connection. |
| UpdateHandler.markAsUsed(mContext, mClientId, mWordlistId, mVersion, mStatus, true); |
| if (MetadataDbHelper.STATUS_AVAILABLE == mStatus) { |
| setStatus(MetadataDbHelper.STATUS_DOWNLOADING); |
| } else if (MetadataDbHelper.STATUS_DISABLED == mStatus |
| || MetadataDbHelper.STATUS_DELETING == mStatus) { |
| // If the status is DELETING, it means Android Keyboard |
| // has not deleted the word list yet, so we can safely |
| // turn it to 'installed'. The status DISABLED is still supported internally to |
| // avoid breaking older installations and all but there should not be a way to |
| // disable a word list through the interface any more. |
| setStatus(MetadataDbHelper.STATUS_INSTALLED); |
| } else { |
| Log.e(TAG, "Unexpected state of the word list for enabling " + mStatus); |
| } |
| } |
| private void deleteDict() { |
| SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext); |
| CommonPreferences.disable(prefs, mWordlistId); |
| setStatus(MetadataDbHelper.STATUS_DELETING); |
| UpdateHandler.markAsDeleting(mContext, mClientId, mWordlistId, mVersion, mStatus); |
| } |
| |
| @Override |
| protected void onBindView(final View view) { |
| super.onBindView(view); |
| ((ViewGroup)view).setLayoutTransition(null); |
| |
| final DictionaryDownloadProgressBar progressBar = |
| (DictionaryDownloadProgressBar)view.findViewById(R.id.dictionary_line_progress_bar); |
| final TextView status = (TextView)view.findViewById(android.R.id.summary); |
| progressBar.setIds(mClientId, mWordlistId); |
| progressBar.setMax(mFilesize); |
| final boolean showProgressBar = (MetadataDbHelper.STATUS_DOWNLOADING == mStatus); |
| status.setVisibility(showProgressBar ? View.INVISIBLE : View.VISIBLE); |
| progressBar.setVisibility(showProgressBar ? View.VISIBLE : View.INVISIBLE); |
| |
| final ButtonSwitcher buttonSwitcher = |
| (ButtonSwitcher)view.findViewById(R.id.wordlist_button_switcher); |
| if (mInterfaceState.isOpen(mWordlistId)) { |
| // The button is open. |
| final int previousStatus = mInterfaceState.getStatus(mWordlistId); |
| buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(previousStatus)); |
| if (previousStatus != mStatus) { |
| // We come here if the status has changed since last time. We need to animate |
| // the transition. |
| buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus)); |
| mInterfaceState.setOpen(mWordlistId, mStatus); |
| } |
| } else { |
| // The button is closed. |
| buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON); |
| } |
| buttonSwitcher.setInternalOnClickListener(mActionButtonClickHandler); |
| view.setOnClickListener(mPreferenceClickHandler); |
| } |
| |
| private class OnWordListPreferenceClick implements View.OnClickListener { |
| @Override |
| public void onClick(final View v) { |
| // Note : v is the preference view |
| final ViewParent parent = v.getParent(); |
| // Just in case something changed in the framework, test for the concrete class |
| if (!(parent instanceof ListView)) return; |
| final ListView listView = (ListView)parent; |
| final int indexToOpen; |
| // Close all first, we'll open back any item that needs to be open. |
| final boolean wasOpen = mInterfaceState.isOpen(mWordlistId); |
| mInterfaceState.closeAll(); |
| if (wasOpen) { |
| // This button being shown. Take note that we don't want to open any button in the |
| // loop below. |
| indexToOpen = -1; |
| } else { |
| // This button was not being shown. Open it, and remember the index of this |
| // child as the one to open in the following loop. |
| mInterfaceState.setOpen(mWordlistId, mStatus); |
| indexToOpen = listView.indexOfChild(v); |
| } |
| final int lastDisplayedIndex = |
| listView.getLastVisiblePosition() - listView.getFirstVisiblePosition(); |
| // The "lastDisplayedIndex" is actually displayed, hence the <= |
| for (int i = 0; i <= lastDisplayedIndex; ++i) { |
| final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)listView.getChildAt(i) |
| .findViewById(R.id.wordlist_button_switcher); |
| if (i == indexToOpen) { |
| buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus)); |
| } else { |
| buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON); |
| } |
| } |
| } |
| } |
| |
| private class OnActionButtonClick implements View.OnClickListener { |
| @Override |
| public void onClick(final View v) { |
| switch (getActionIdFromStatusAndMenuEntry(mStatus)) { |
| case ACTION_ENABLE_DICT: |
| enableDict(); |
| break; |
| case ACTION_DISABLE_DICT: |
| disableDict(); |
| break; |
| case ACTION_DELETE_DICT: |
| deleteDict(); |
| break; |
| default: |
| Log.e(TAG, "Unknown menu item pressed"); |
| } |
| } |
| } |
| } |