Home | History | Annotate | Download | only in dictionarypack
      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 
     17 package com.android.inputmethod.dictionarypack;
     18 
     19 import android.content.Context;
     20 import android.content.SharedPreferences;
     21 import android.preference.Preference;
     22 import android.util.Log;
     23 import android.view.View;
     24 import android.view.ViewGroup;
     25 import android.view.ViewParent;
     26 import android.widget.ListView;
     27 import android.widget.TextView;
     28 
     29 import com.android.inputmethod.latin.R;
     30 
     31 import java.util.Locale;
     32 
     33 /**
     34  * A preference for one word list.
     35  *
     36  * This preference refers to a single word list, as available in the dictionary
     37  * pack. Upon being pressed, it displays a menu to allow the user to install, disable,
     38  * enable or delete it as appropriate for the current state of the word list.
     39  */
     40 public final class WordListPreference extends Preference {
     41     private static final String TAG = WordListPreference.class.getSimpleName();
     42 
     43     // What to display in the "status" field when we receive unknown data as a status from
     44     // the content provider. Empty string sounds sensible.
     45     private static final String NO_STATUS_MESSAGE = "";
     46 
     47     /// Actions
     48     private static final int ACTION_UNKNOWN = 0;
     49     private static final int ACTION_ENABLE_DICT = 1;
     50     private static final int ACTION_DISABLE_DICT = 2;
     51     private static final int ACTION_DELETE_DICT = 3;
     52 
     53     // Members
     54     // The metadata word list id and version of this word list.
     55     public final String mWordlistId;
     56     public final int mVersion;
     57     public final Locale mLocale;
     58     public final String mDescription;
     59 
     60     // The id of the client for which this preference is.
     61     private final String mClientId;
     62     // The status
     63     private int mStatus;
     64     // The size of the dictionary file
     65     private final int mFilesize;
     66 
     67     private final DictionaryListInterfaceState mInterfaceState;
     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, final int filesize) {
     73         super(context, null);
     74         mInterfaceState = dictionaryListInterfaceState;
     75         mClientId = clientId;
     76         mVersion = version;
     77         mWordlistId = wordlistId;
     78         mFilesize = filesize;
     79         mLocale = locale;
     80         mDescription = description;
     81 
     82         setLayoutResource(R.layout.dictionary_line);
     83 
     84         setTitle(description);
     85         setStatus(status);
     86         setKey(wordlistId);
     87     }
     88 
     89     public void setStatus(final int status) {
     90         if (status == mStatus) return;
     91         mStatus = status;
     92         setSummary(getSummary(status));
     93     }
     94 
     95     public boolean hasStatus(final int status) {
     96         return status == mStatus;
     97     }
     98 
     99     @Override
    100     public View onCreateView(final ViewGroup parent) {
    101         final View orphanedView = mInterfaceState.findFirstOrphanedView();
    102         if (null != orphanedView) return orphanedView; // Will be sent to onBindView
    103         final View newView = super.onCreateView(parent);
    104         return mInterfaceState.addToCacheAndReturnView(newView);
    105     }
    106 
    107     public boolean hasPriorityOver(final int otherPrefStatus) {
    108         // Both of these should be one of MetadataDbHelper.STATUS_*
    109         return mStatus > otherPrefStatus;
    110     }
    111 
    112     private String getSummary(final int status) {
    113         final Context context = getContext();
    114         switch (status) {
    115         // If we are deleting the word list, for the user it's like it's already deleted.
    116         // It should be reinstallable. Exposing to the user the whole complexity of
    117         // the delayed deletion process between the dictionary pack and Android Keyboard
    118         // would only be confusing.
    119         case MetadataDbHelper.STATUS_DELETING:
    120         case MetadataDbHelper.STATUS_AVAILABLE:
    121             return context.getString(R.string.dictionary_available);
    122         case MetadataDbHelper.STATUS_DOWNLOADING:
    123             return context.getString(R.string.dictionary_downloading);
    124         case MetadataDbHelper.STATUS_INSTALLED:
    125             return context.getString(R.string.dictionary_installed);
    126         case MetadataDbHelper.STATUS_DISABLED:
    127             return context.getString(R.string.dictionary_disabled);
    128         default:
    129             return NO_STATUS_MESSAGE;
    130         }
    131     }
    132 
    133     // The table below needs to be kept in sync with MetadataDbHelper.STATUS_* since it uses
    134     // the values as indices.
    135     private static final int sStatusActionList[][] = {
    136         // MetadataDbHelper.STATUS_UNKNOWN
    137         {},
    138         // MetadataDbHelper.STATUS_AVAILABLE
    139         { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT },
    140         // MetadataDbHelper.STATUS_DOWNLOADING
    141         { ButtonSwitcher.STATUS_CANCEL, ACTION_DISABLE_DICT },
    142         // MetadataDbHelper.STATUS_INSTALLED
    143         { ButtonSwitcher.STATUS_DELETE, ACTION_DELETE_DICT },
    144         // MetadataDbHelper.STATUS_DISABLED
    145         { ButtonSwitcher.STATUS_DELETE, ACTION_DELETE_DICT },
    146         // MetadataDbHelper.STATUS_DELETING
    147         // We show 'install' because the file is supposed to be deleted.
    148         // The user may reinstall it.
    149         { ButtonSwitcher.STATUS_INSTALL, ACTION_ENABLE_DICT }
    150     };
    151 
    152     static int getButtonSwitcherStatus(final int status) {
    153         if (status >= sStatusActionList.length) {
    154             Log.e(TAG, "Unknown status " + status);
    155             return ButtonSwitcher.STATUS_NO_BUTTON;
    156         }
    157         return sStatusActionList[status][0];
    158     }
    159 
    160     static int getActionIdFromStatusAndMenuEntry(final int status) {
    161         if (status >= sStatusActionList.length) {
    162             Log.e(TAG, "Unknown status " + status);
    163             return ACTION_UNKNOWN;
    164         }
    165         return sStatusActionList[status][1];
    166     }
    167 
    168     private void disableDict() {
    169         final Context context = getContext();
    170         final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
    171         CommonPreferences.disable(prefs, mWordlistId);
    172         UpdateHandler.markAsUnused(context, mClientId, mWordlistId, mVersion, mStatus);
    173         if (MetadataDbHelper.STATUS_DOWNLOADING == mStatus) {
    174             setStatus(MetadataDbHelper.STATUS_AVAILABLE);
    175         } else if (MetadataDbHelper.STATUS_INSTALLED == mStatus) {
    176             // Interface-wise, we should no longer be able to come here. However, this is still
    177             // the right thing to do if we do come here.
    178             setStatus(MetadataDbHelper.STATUS_DISABLED);
    179         } else {
    180             Log.e(TAG, "Unexpected state of the word list for disabling " + mStatus);
    181         }
    182     }
    183 
    184     private void enableDict() {
    185         final Context context = getContext();
    186         final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
    187         CommonPreferences.enable(prefs, mWordlistId);
    188         // Explicit enabling by the user : allow downloading on metered data connection.
    189         UpdateHandler.markAsUsed(context, mClientId, mWordlistId, mVersion, mStatus, true);
    190         if (MetadataDbHelper.STATUS_AVAILABLE == mStatus) {
    191             setStatus(MetadataDbHelper.STATUS_DOWNLOADING);
    192         } else if (MetadataDbHelper.STATUS_DISABLED == mStatus
    193                 || MetadataDbHelper.STATUS_DELETING == mStatus) {
    194             // If the status is DELETING, it means Android Keyboard
    195             // has not deleted the word list yet, so we can safely
    196             // turn it to 'installed'. The status DISABLED is still supported internally to
    197             // avoid breaking older installations and all but there should not be a way to
    198             // disable a word list through the interface any more.
    199             setStatus(MetadataDbHelper.STATUS_INSTALLED);
    200         } else {
    201             Log.e(TAG, "Unexpected state of the word list for enabling " + mStatus);
    202         }
    203     }
    204 
    205     private void deleteDict() {
    206         final Context context = getContext();
    207         final SharedPreferences prefs = CommonPreferences.getCommonPreferences(context);
    208         CommonPreferences.disable(prefs, mWordlistId);
    209         setStatus(MetadataDbHelper.STATUS_DELETING);
    210         UpdateHandler.markAsDeleting(context, mClientId, mWordlistId, mVersion, mStatus);
    211     }
    212 
    213     @Override
    214     protected void onBindView(final View view) {
    215         super.onBindView(view);
    216         ((ViewGroup)view).setLayoutTransition(null);
    217 
    218         final DictionaryDownloadProgressBar progressBar =
    219                 (DictionaryDownloadProgressBar)view.findViewById(R.id.dictionary_line_progress_bar);
    220         final TextView status = (TextView)view.findViewById(android.R.id.summary);
    221         progressBar.setIds(mClientId, mWordlistId);
    222         progressBar.setMax(mFilesize);
    223         final boolean showProgressBar = (MetadataDbHelper.STATUS_DOWNLOADING == mStatus);
    224         setSummary(getSummary(mStatus));
    225         status.setVisibility(showProgressBar ? View.INVISIBLE : View.VISIBLE);
    226         progressBar.setVisibility(showProgressBar ? View.VISIBLE : View.INVISIBLE);
    227 
    228         final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)view.findViewById(
    229                 R.id.wordlist_button_switcher);
    230         // We need to clear the state of the button switcher, because we reuse views; if we didn't
    231         // reset it would animate from whatever its old state was.
    232         buttonSwitcher.reset(mInterfaceState);
    233         if (mInterfaceState.isOpen(mWordlistId)) {
    234             // The button is open.
    235             final int previousStatus = mInterfaceState.getStatus(mWordlistId);
    236             buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(previousStatus));
    237             if (previousStatus != mStatus) {
    238                 // We come here if the status has changed since last time. We need to animate
    239                 // the transition.
    240                 buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus));
    241                 mInterfaceState.setOpen(mWordlistId, mStatus);
    242             }
    243         } else {
    244             // The button is closed.
    245             buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON);
    246         }
    247         buttonSwitcher.setInternalOnClickListener(new View.OnClickListener() {
    248             @Override
    249             public void onClick(final View v) {
    250                 onActionButtonClicked();
    251             }
    252         });
    253         view.setOnClickListener(new View.OnClickListener() {
    254             @Override
    255             public void onClick(final View v) {
    256                 onWordListClicked(v);
    257             }
    258         });
    259     }
    260 
    261     void onWordListClicked(final View v) {
    262         // Note : v is the preference view
    263         final ViewParent parent = v.getParent();
    264         // Just in case something changed in the framework, test for the concrete class
    265         if (!(parent instanceof ListView)) return;
    266         final ListView listView = (ListView)parent;
    267         final int indexToOpen;
    268         // Close all first, we'll open back any item that needs to be open.
    269         final boolean wasOpen = mInterfaceState.isOpen(mWordlistId);
    270         mInterfaceState.closeAll();
    271         if (wasOpen) {
    272             // This button being shown. Take note that we don't want to open any button in the
    273             // loop below.
    274             indexToOpen = -1;
    275         } else {
    276             // This button was not being shown. Open it, and remember the index of this
    277             // child as the one to open in the following loop.
    278             mInterfaceState.setOpen(mWordlistId, mStatus);
    279             indexToOpen = listView.indexOfChild(v);
    280         }
    281         final int lastDisplayedIndex =
    282                 listView.getLastVisiblePosition() - listView.getFirstVisiblePosition();
    283         // The "lastDisplayedIndex" is actually displayed, hence the <=
    284         for (int i = 0; i <= lastDisplayedIndex; ++i) {
    285             final ButtonSwitcher buttonSwitcher = (ButtonSwitcher)listView.getChildAt(i)
    286                     .findViewById(R.id.wordlist_button_switcher);
    287             if (i == indexToOpen) {
    288                 buttonSwitcher.setStatusAndUpdateVisuals(getButtonSwitcherStatus(mStatus));
    289             } else {
    290                 buttonSwitcher.setStatusAndUpdateVisuals(ButtonSwitcher.STATUS_NO_BUTTON);
    291             }
    292         }
    293     }
    294 
    295     void onActionButtonClicked() {
    296         switch (getActionIdFromStatusAndMenuEntry(mStatus)) {
    297         case ACTION_ENABLE_DICT:
    298             enableDict();
    299             break;
    300         case ACTION_DISABLE_DICT:
    301             disableDict();
    302             break;
    303         case ACTION_DELETE_DICT:
    304             deleteDict();
    305             break;
    306         default:
    307             Log.e(TAG, "Unknown menu item pressed");
    308         }
    309     }
    310 }
    311