1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
|
/**
* 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 org.kelar.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 org.kelar.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 {
private static final 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.
private static final String NO_STATUS_MESSAGE = "";
/// Actions
private static final int ACTION_UNKNOWN = 0;
private static final int ACTION_ENABLE_DICT = 1;
private static final int ACTION_DISABLE_DICT = 2;
private static final int ACTION_DELETE_DICT = 3;
// Members
// The metadata word list id and version of this word list.
public final String mWordlistId;
public final int mVersion;
public final Locale mLocale;
public final String mDescription;
// The id of the client for which this preference is.
private final String mClientId;
// The status
private int mStatus;
// The size of the dictionary file
private final int mFilesize;
private final DictionaryListInterfaceState mInterfaceState;
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);
mInterfaceState = dictionaryListInterfaceState;
mClientId = clientId;
mVersion = version;
mWordlistId = wordlistId;
mFilesize = filesize;
mLocale = locale;
mDescription = description;
setLayoutResource(R.layout.dictionary_line);
setTitle(description);
setStatus(status);
setKey(wordlistId);
}
public void setStatus(final int status) {
if (status == mStatus) return;
mStatus = status;
setSummary(getSummary(status));
}
public boolean hasStatus(final int status) {
return status == mStatus;
}
@Override
public View onCreateView(final ViewGroup parent) {
final View orphanedView = mInterfaceState.findFirstOrphanedView();
if (null != orphanedView) return orphanedView; // Will be sent to onBindView
final View newView = super.onCreateView(parent);
return mInterfaceState.addToCacheAndReturnView(newView);
}
public boolean hasPriorityOver(final int otherPrefStatus) {
// Both of these should be one of MetadataDbHelper.STATUS_*
return mStatus > otherPrefStatus;
}
private String getSummary(final int status) {
final Context context = getContext();
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 Kelar Keyboard
// would only be confusing.
case MetadataDbHelper.STATUS_DELETING:
case MetadataDbHelper.STATUS_AVAILABLE:
return context.getString(R.string.dictionary_available);
case MetadataDbHelper.STATUS_DOWNLOADING:
return context.getString(R.string.dictionary_downloading);
case MetadataDbHelper.STATUS_INSTALLED:
return context.getString(R.string.dictionary_installed);
case MetadataDbHelper.STATUS_DISABLED:
return context.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 }
};
static int getButtonSwitcherStatus(final int status) {
if (status >= sStatusActionList.length) {
Log.e(TAG, "Unknown status " + status);
return ButtonSwitcher.STATUS_NO_BUTTON;
}
return sStatusActionList[status][0];
}
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() {
final Context context = getContext();
final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
CommonPreferences.disable(prefs, mWordlistId);
UpdateHandler.markAsUnused(context, 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() {
final Context context = getContext();
final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
CommonPreferences.enable(prefs, mWordlistId);
// Explicit enabling by the user : allow downloading on metered data connection.
UpdateHandler.markAsUsed(context, 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 Kelar 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() {
final Context context = getContext();
final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
CommonPreferences.disable(prefs, mWordlistId);
setStatus(MetadataDbHelper.STATUS_DELETING);
UpdateHandler.markAsDeleting(context, 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);
setSummary(getSummary(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);
// We need to clear the state of the button switcher, because we reuse views; if we didn't
// reset it would animate from whatever its old state was.
buttonSwitcher.reset(mInterfaceState);
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(new View.OnClickListener() {
@Override
public void onClick(final View v) {
onActionButtonClicked();
}
});
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
onWordListClicked(v);
}
});
}
void onWordListClicked(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);
}
}
}
void onActionButtonClicked() {
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");
}
}
}
|