WordListPreference.java revision 513c63e877320bca4860dadc88e3a14ffb861e36
1/** 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17package com.android.inputmethod.dictionarypack; 18 19import android.content.Context; 20import android.content.SharedPreferences; 21import android.preference.Preference; 22import android.util.Log; 23import android.view.View; 24import android.view.ViewGroup; 25import android.view.ViewParent; 26import android.widget.ListView; 27 28import com.android.inputmethod.latin.R; 29 30import java.util.Locale; 31 32/** 33 * A preference for one word list. 34 * 35 * This preference refers to a single word list, as available in the dictionary 36 * pack. Upon being pressed, it displays a menu to allow the user to install, disable, 37 * enable or delete it as appropriate for the current state of the word list. 38 */ 39public final class WordListPreference extends Preference { 40 static final private String TAG = WordListPreference.class.getSimpleName(); 41 42 // What to display in the "status" field when we receive unknown data as a status from 43 // the content provider. Empty string sounds sensible. 44 static final private String NO_STATUS_MESSAGE = ""; 45 46 /// Actions 47 static final private int ACTION_UNKNOWN = 0; 48 static final private int ACTION_ENABLE_DICT = 1; 49 static final private int ACTION_DISABLE_DICT = 2; 50 static final private int ACTION_DELETE_DICT = 3; 51 52 // Members 53 // The context to get resources 54 final Context mContext; 55 // The id of the client for which this preference is. 56 final String mClientId; 57 // The metadata word list id and version of this word list. 58 public final String mWordlistId; 59 public final int mVersion; 60 // The status 61 public int mStatus; 62 63 private final DictionaryListInterfaceState mInterfaceState; 64 private final OnWordListPreferenceClick mPreferenceClickHandler = 65 new OnWordListPreferenceClick(); 66 private final OnActionButtonClick mActionButtonClickHandler = 67 new OnActionButtonClick(); 68 69 public WordListPreference(final Context context, 70 final DictionaryListInterfaceState dictionaryListInterfaceState, final String clientId, 71 final String wordlistId, final int version, final Locale locale, 72 final String description, final int status) { 73 super(context, null); 74 mContext = context; 75 mInterfaceState = dictionaryListInterfaceState; 76 mClientId = clientId; 77 mVersion = version; 78 mWordlistId = wordlistId; 79 80 setLayoutResource(R.layout.dictionary_line); 81 82 setTitle(description); 83 setStatus(status); 84 setKey(wordlistId); 85 } 86 87 private void setStatus(final int status) { 88 if (status == mStatus) return; 89 mStatus = status; 90 setSummary(getSummary(status)); 91 } 92 93 private String getSummary(final int status) { 94 switch (status) { 95 // If we are deleting the word list, for the user it's like it's already deleted. 96 // It should be reinstallable. Exposing to the user the whole complexity of 97 // the delayed deletion process between the dictionary pack and Android Keyboard 98 // would only be confusing. 99 case MetadataDbHelper.STATUS_DELETING: 100 case MetadataDbHelper.STATUS_AVAILABLE: 101 return mContext.getString(R.string.dictionary_available); 102 case MetadataDbHelper.STATUS_DOWNLOADING: 103 return mContext.getString(R.string.dictionary_downloading); 104 case MetadataDbHelper.STATUS_INSTALLED: 105 return mContext.getString(R.string.dictionary_installed); 106 case MetadataDbHelper.STATUS_DISABLED: 107 return mContext.getString(R.string.dictionary_disabled); 108 default: 109 return NO_STATUS_MESSAGE; 110 } 111 } 112 113 // The table below needs to be kept in sync with MetadataDbHelper.STATUS_* since it uses 114 // the values as indices. 115 private static final int sStatusActionList[][] = { 116 // MetadataDbHelper.STATUS_UNKNOWN 117 {}, 118 // MetadataDbHelper.STATUS_AVAILABLE 119 { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT }, 120 // MetadataDbHelper.STATUS_DOWNLOADING 121 { ButtonSwitcher.STATUS_CANCEL, ACTION_DISABLE_DICT }, 122 // MetadataDbHelper.STATUS_INSTALLED 123 { ButtonSwitcher.STATUS_DELETE, ACTION_DELETE_DICT }, 124 // MetadataDbHelper.STATUS_DISABLED 125 { ButtonSwitcher.STATUS_DELETE, ACTION_DELETE_DICT }, 126 // MetadataDbHelper.STATUS_DELETING 127 // We show 'install' because the file is supposed to be deleted. 128 // The user may reinstall it. 129 { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT } 130 }; 131 132 private int getButtonSwitcherStatus(final int status) { 133 if (status >= sStatusActionList.length) { 134 Log.e(TAG, "Unknown status " + status); 135 return ButtonSwitcher.STATUS_NO_BUTTON; 136 } 137 return sStatusActionList[status][0]; 138 } 139 140 private static int getActionIdFromStatusAndMenuEntry(final int status) { 141 if (status >= sStatusActionList.length) { 142 Log.e(TAG, "Unknown status " + status); 143 return ACTION_UNKNOWN; 144 } 145 return sStatusActionList[status][1]; 146 } 147 148 private void disableDict() { 149 SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext); 150 CommonPreferences.disable(prefs, mWordlistId); 151 UpdateHandler.markAsUnused(mContext, mClientId, mWordlistId, mVersion, mStatus); 152 if (MetadataDbHelper.STATUS_DOWNLOADING == mStatus) { 153 setStatus(MetadataDbHelper.STATUS_AVAILABLE); 154 } else if (MetadataDbHelper.STATUS_INSTALLED == mStatus) { 155 // Interface-wise, we should no longer be able to come here. However, this is still 156 // the right thing to do if we do come here. 157 setStatus(MetadataDbHelper.STATUS_DISABLED); 158 } else { 159 Log.e(TAG, "Unexpected state of the word list for disabling " + mStatus); 160 } 161 } 162 private void enableDict() { 163 SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext); 164 CommonPreferences.enable(prefs, mWordlistId); 165 // Explicit enabling by the user : allow downloading on metered data connection. 166 UpdateHandler.markAsUsed(mContext, mClientId, mWordlistId, mVersion, mStatus, true); 167 if (MetadataDbHelper.STATUS_AVAILABLE == mStatus) { 168 setStatus(MetadataDbHelper.STATUS_DOWNLOADING); 169 } else if (MetadataDbHelper.STATUS_DISABLED == mStatus 170 || MetadataDbHelper.STATUS_DELETING == mStatus) { 171 // If the status is DELETING, it means Android Keyboard 172 // has not deleted the word list yet, so we can safely 173 // turn it to 'installed'. The status DISABLED is still supported internally to 174 // avoid breaking older installations and all but there should not be a way to 175 // disable a word list through the interface any more. 176 setStatus(MetadataDbHelper.STATUS_INSTALLED); 177 } else { 178 Log.e(TAG, "Unexpected state of the word list for enabling " + mStatus); 179 } 180 } 181 private void deleteDict() { 182 SharedPreferences prefs = CommonPreferences.getCommonPreferences(mContext); 183 CommonPreferences.disable(prefs, mWordlistId); 184 setStatus(MetadataDbHelper.STATUS_DELETING); 185 UpdateHandler.markAsDeleting(mContext, mClientId, mWordlistId, mVersion, mStatus); 186 } 187 188 @Override 189 protected void onBindView(final View view) { 190 super.onBindView(view); 191 ((ViewGroup)view).setLayoutTransition(null); 192 final ButtonSwitcher buttonSwitcher = 193 (ButtonSwitcher)view.findViewById(R.id.wordlist_button_switcher); 194 if (mInterfaceState.isOpen(mWordlistId)) { 195 // The button is open. 196 final int previousStatus = mInterfaceState.getStatus(mWordlistId); 197 buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(previousStatus)); 198 if (previousStatus != mStatus) { 199 // We come here if the status has changed since last time. We need to animate 200 // the transition. 201 buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus)); 202 mInterfaceState.setOpen(mWordlistId, mStatus); 203 } 204 } else { 205 // The button is closed. 206 buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON); 207 } 208 buttonSwitcher.setInternalOnClickListener(mActionButtonClickHandler); 209 view.setOnClickListener(mPreferenceClickHandler); 210 } 211 212 private class OnWordListPreferenceClick implements View.OnClickListener { 213 @Override 214 public void onClick(final View v) { 215 // Note : v is the preference view 216 final ViewParent parent = v.getParent(); 217 // Just in case something changed in the framework, test for the concrete class 218 if (!(parent instanceof ListView)) return; 219 final ListView listView = (ListView)parent; 220 final int indexToOpen; 221 // Close all first, we'll open back any item that needs to be open. 222 mInterfaceState.closeAll(); 223 if (mInterfaceState.isOpen(mWordlistId)) { 224 // This button being shown. Take note that we don't want to open any button in the 225 // loop below. 226 indexToOpen = -1; 227 } else { 228 // This button was not being shown. Open it, and remember the index of this 229 // child as the one to open in the following loop. 230 mInterfaceState.setOpen(mWordlistId, mStatus); 231 indexToOpen = listView.indexOfChild(v); 232 } 233 final int lastDisplayedIndex = 234 listView.getLastVisiblePosition() - listView.getFirstVisiblePosition(); 235 // The "lastDisplayedIndex" is actually displayed, hence the <= 236 for (int i = 0; i <= lastDisplayedIndex; ++i) { 237 final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)listView.getChildAt(i) 238 .findViewById(R.id.wordlist_button_switcher); 239 if (i == indexToOpen) { 240 buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus)); 241 } else { 242 buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON); 243 } 244 } 245 } 246 } 247 248 private class OnActionButtonClick implements View.OnClickListener { 249 @Override 250 public void onClick(final View v) { 251 switch (getActionIdFromStatusAndMenuEntry(mStatus)) { 252 case ACTION_ENABLE_DICT: 253 enableDict(); 254 break; 255 case ACTION_DISABLE_DICT: 256 disableDict(); 257 break; 258 case ACTION_DELETE_DICT: 259 deleteDict(); 260 break; 261 default: 262 Log.e(TAG, "Unknown menu item pressed"); 263 } 264 } 265 } 266} 267