Implement multiple locales and sublocales in Settings

Does drag-and-drop reordering, supports adding / removing locales,
suggestions, search, removes locales already selected in the
user preferences.

Bug: 25800339
Change-Id: Iffe7b9810c77ec93a84d848ab20b2ba405249676
This commit is contained in:
Mihai Nita
2016-01-12 08:55:05 -08:00
parent ae7e15b730
commit d7d48fda53
14 changed files with 747 additions and 142 deletions

View File

@@ -567,7 +567,7 @@
<category android:name="android.intent.category.VOICE_LAUNCH" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.LocalePicker" />
android:value="com.android.settings.localepicker.LocaleListEditor" />
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="50dp"
android:width="50dp"
android:viewportHeight="100"
android:viewportWidth="100"
android:tint="#757575" >
<path android:fillColor="@android:color/white"
android:pathData="M29,48 l 0,-4 42,0 0,4 z" />
<path android:fillColor="@android:color/white"
android:pathData="M29,56 l 0,-4 42,0 0,4 z" />
</vector>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<com.android.settings.localepicker.LocaleDragCell
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:baselineAligned="false"
android:layoutDirection="locale"
android:textDirection="locale">
<TextView
android:id="@+id/miniLabel"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
tools:text="22"
android:textColor="?android:attr/colorAccent"
android:minWidth="20sp"
android:textAlignment="textEnd"
android:gravity="center_vertical|end"
android:background="?android:colorBackground"/>
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="20sp"
android:layout_marginLeft="6dp"
android:layout_marginRight="6dp"
android:visibility="gone"
android:background="?android:colorBackground"/>
<TextView
android:id="@+id/label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:padding="12dp"
tools:text="France"
android:textAppearance="?android:attr/textAppearanceListItem"
android:background="?android:colorBackground"/>
<ImageView
android:id="@+id/l10nWarn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="@android:drawable/stat_sys_warning"
android:focusableInTouchMode="false"
android:focusable="false"
android:paddingStart="12dp"
android:paddingEnd="12dp"
android:tint="?android:attr/colorAccent"/>
<ImageView
android:id="@+id/dragHandle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:src="@drawable/drag_handle"/>
</com.android.settings.localepicker.LocaleDragCell>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layoutDirection="locale"
android:orientation="vertical"
android:textDirection="locale">
<com.android.settings.localepicker.LocaleRecyclerView
android:id="@+id/dragList"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
<TextView
android:id="@+id/add_language"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:contextClickable="true"
android:drawableStart="@drawable/ic_add_24dp"
android:drawablePadding="12dp"
android:gravity="center_vertical"
android:measureWithLargestChild="false"
android:padding="16dp"
android:text="@string/add_a_language"
android:textAppearance="?android:attr/textAppearanceListItem"/>
</LinearLayout>

View File

@@ -513,8 +513,27 @@
<string name="next_label">Next</string>
<!-- LocalePicker -->
<!-- Title for the locale picker activity -->
<string name="language_picker_title">Language</string>
<!-- Title for the language selection screen [CHAR LIMIT=25] -->
<string name="pref_title_lang_selection">Language preference</string>
<!-- Menu item in the locale menu. Will remove the selected locales. [CHAR LIMIT=30] -->
<string name="locale_remove_menu">Remove</string>
<!-- "Button" that opens a language picker. The selected language gets added to the language list. [CHAR LIMIT=30] -->
<string name="add_a_language">Add a language</string>
<!-- The title of the confirmation dialog shown when the user selected several languages and tries to remove them [CHAR LIMIT=60] -->
<string name="dlg_remove_locales_title">Remove from your language preference?</string>
<!-- TODO: come up with a good string -->
<!-- The text of the confirmation dialog shown when the user selected several languages and tries to remove them [CHAR LIMIT=NONE] -->
<string name="dlg_remove_locales_message" translatable="false">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla egestas porta aliquet. Ut laoreet orci tellus, id cursus enim volutpat in.</string>
<!-- The title of the error dialog shown when the user selected all the languages and tries to remove them [CHAR LIMIT=60] -->
<string name="dlg_remove_locales_error_title">Language removal error</string>
<!-- The text of the error dialog shown when the user selected all the languages and tries to remove them [CHAR LIMIT=NONE] -->
<string name="dlg_remove_locales_error_message">Cant remove all the languages, you should leave at least one.</string>
<!-- The title of the dialog to pick an activity. This is shown when there are multiple activities that can do a particular action. For example, suppose you click on the "Share" menu item in the Browser. Since you can share the webpage URL via many communication methods, this dialog would come up with choices like "Email", "IM", etc. This is a generic message, and the previous example is a single possible scenario (so please don't assume it's for the browser or anything :) ). -->
<string name="activity_picker_label">Choose activity</string>

View File

@@ -22,7 +22,7 @@
<PreferenceScreen
android:key="phone_language"
android:title="@string/phone_language"
android:fragment="com.android.settings.LocalePicker"
android:fragment="com.android.settings.localepicker.LocaleListEditor"
/>
<!-- Spell checker preference title, summary and fragment will be set programmatically. -->

View File

@@ -37,6 +37,7 @@ public abstract class InstrumentedFragment extends PreferenceFragment {
public static final int DATA_USAGE_LIST = UNDECLARED + 7;
public static final int BILLING_CYCLE = UNDECLARED + 8;
public static final int APP_DATA_USAGE = UNDECLARED + 9;
public static final int USER_LOCALE_LIST = UNDECLARED + 10;
/**
* Declare the view of this category.

View File

@@ -1,115 +0,0 @@
/*
* Copyright (C) 2010 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.settings;
import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
import java.util.Locale;
public class LocalePicker extends com.android.internal.app.LocalePickerWithRegion
implements com.android.internal.app.LocalePickerWithRegion.LocaleSelectionListener,
DialogCreatable {
private static final String TAG = "LocalePicker";
private SettingsDialogFragment mDialogFragment;
private static final int DLG_SHOW_GLOBAL_WARNING = 1;
private static final String SAVE_TARGET_LOCALE = "locale";
private Locale mTargetLocale;
public LocalePicker() {
super();
setLocaleSelectionListener(this);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(SAVE_TARGET_LOCALE)) {
mTargetLocale = new Locale(savedInstanceState.getString(SAVE_TARGET_LOCALE));
}
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = super.onCreateView(inflater, container, savedInstanceState);
final ListView list = (ListView) view.findViewById(android.R.id.list);
Utils.forcePrepareCustomPreferencesList(container, view, list, false);
return view;
}
@Override
public void onLocaleSelected(final Locale locale) {
if (Utils.hasMultipleUsers(getActivity())) {
mTargetLocale = locale;
showDialog(DLG_SHOW_GLOBAL_WARNING);
} else {
getActivity().onBackPressed();
com.android.internal.app.LocalePicker.updateLocale(locale);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mTargetLocale != null) {
outState.putString(SAVE_TARGET_LOCALE, mTargetLocale.toString());
}
}
protected void showDialog(int dialogId) {
if (mDialogFragment != null) {
Log.e(TAG, "Old dialog fragment not null!");
}
mDialogFragment = new SettingsDialogFragment(this, dialogId);
mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId));
}
public Dialog onCreateDialog(final int dialogId) {
return Utils.buildGlobalChangeWarningDialog(getActivity(),
R.string.global_locale_change_title,
new Runnable() {
public void run() {
removeDialog(dialogId);
getActivity().onBackPressed();
com.android.internal.app.LocalePicker.updateLocale(mTargetLocale);
}
}
);
}
protected void removeDialog(int dialogId) {
// mDialogFragment may not be visible yet in parent fragment's onResume().
// To be able to dismiss dialog at that time, don't check
// mDialogFragment.isVisible().
if (mDialogFragment != null && mDialogFragment.getDialogId() == dialogId) {
mDialogFragment.dismiss();
}
mDialogFragment = null;
}
}

View File

@@ -80,6 +80,7 @@ import com.android.settings.inputmethod.InputMethodAndLanguageSettings;
import com.android.settings.inputmethod.KeyboardLayoutPickerFragment;
import com.android.settings.inputmethod.SpellCheckersSettings;
import com.android.settings.inputmethod.UserDictionaryList;
import com.android.settings.localepicker.LocaleListEditor;
import com.android.settings.location.LocationSettings;
import com.android.settings.nfc.AndroidBeam;
import com.android.settings.nfc.PaymentSettings;
@@ -246,7 +247,7 @@ public class SettingsActivity extends SettingsDrawerActivity
WifiP2pSettings.class.getName(),
VpnSettings.class.getName(),
DateTimeSettings.class.getName(),
LocalePicker.class.getName(),
LocaleListEditor.class.getName(),
InputMethodAndLanguageSettings.class.getName(),
SpellCheckersSettings.class.getName(),
UserDictionaryList.class.getName(),

View File

@@ -43,6 +43,7 @@ import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceManager;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import android.util.LocaleList;
import android.view.InputDevice;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
@@ -50,6 +51,7 @@ import android.view.inputmethod.InputMethodSubtype;
import android.view.textservice.SpellCheckerInfo;
import android.view.textservice.TextServicesManager;
import com.android.internal.app.LocaleHelper;
import com.android.internal.app.LocalePicker;
import com.android.internal.logging.MetricsLogger;
import com.android.settings.R;
@@ -266,8 +268,8 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
if (!mShowsOnlyFullImeAndKeyboardList) {
if (mLanguagePref != null) {
String localeName = getLocaleName(getActivity());
mLanguagePref.setSummary(localeName);
String localeNames = getLocaleNames(getActivity());
mLanguagePref.setSummary(localeNames);
}
updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS));
@@ -340,19 +342,9 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
return super.onPreferenceTreeClick(preference);
}
private static String getLocaleName(Context context) {
// We want to show the same string that the LocalePicker used.
// TODO: should this method be in LocalePicker instead?
Locale currentLocale = context.getResources().getConfiguration().locale;
List<LocalePicker.LocaleInfo> locales = LocalePicker.getAllAssetLocales(context, true);
for (LocalePicker.LocaleInfo locale : locales) {
if (locale.getLocale().equals(currentLocale)) {
return locale.getLabel();
}
}
// This can't happen as long as the locale was one set by Settings.
// Fall back in case a developer is testing an unsupported locale.
return currentLocale.getDisplayName(currentLocale);
private static String getLocaleNames(Context context) {
final LocaleList locales = LocalePicker.getLocales();
return LocaleHelper.getDisplayLocaleList(locales, Locale.getDefault());
}
private void saveInputMethodSelectorVisibility(String value) {
@@ -665,8 +657,8 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
@Override
public void setListening(boolean listening) {
if (listening) {
String localeName = getLocaleName(mContext);
mSummaryLoader.setSummary(this, localeName);
String localeNames = getLocaleNames(mContext);
mSummaryLoader.setSummary(this, localeNames);
}
}
}
@@ -690,12 +682,12 @@ public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
// Locale picker.
if (context.getAssets().getLocales().length > 1) {
String localeName = getLocaleName(context);
String localeNames = getLocaleNames(context);
SearchIndexableRaw indexable = new SearchIndexableRaw(context);
indexable.key = KEY_PHONE_LANGUAGE;
indexable.title = context.getString(R.string.phone_language);
indexable.summaryOn = localeName;
indexable.summaryOff = localeName;
indexable.summaryOn = localeNames;
indexable.summaryOff = localeNames;
indexable.screenTitle = screenTitle;
indexables.add(indexable);
}

View File

@@ -0,0 +1,203 @@
/*
* Copyright (C) 2016 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.settings.localepicker;
import android.content.Context;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.LocaleList;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import com.android.settings.R;
import com.android.internal.app.LocalePicker;
import com.android.internal.app.LocaleStore;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
class LocaleDragAndDropAdapter
extends RecyclerView.Adapter<LocaleDragAndDropAdapter.CustomViewHolder> {
private final Context mContext;
private final List<LocaleStore.LocaleInfo> mFeedItemList;
private final ItemTouchHelper mItemTouchHelper;
private boolean mRemoveMode = false;
private boolean mDragEnabled = true;
class CustomViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener {
private final LocaleDragCell mLocaleDragCell;
public CustomViewHolder(LocaleDragCell view) {
super(view);
mLocaleDragCell = view;
mLocaleDragCell.getDragHandle().setOnTouchListener(this);
mLocaleDragCell.getTextLabel().setOnTouchListener(this);
mLocaleDragCell.getTranslateableLabel().setOnTouchListener(this);
}
public LocaleDragCell getLocaleDragCell() {
return mLocaleDragCell;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (mDragEnabled) {
switch (MotionEventCompat.getActionMasked(event)) {
case MotionEvent.ACTION_DOWN:
mItemTouchHelper.startDrag(this);
}
}
return false;
}
}
public LocaleDragAndDropAdapter(Context context, List<LocaleStore.LocaleInfo> feedItemList) {
this.mFeedItemList = feedItemList;
this.mContext = context;
this.mItemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0 /* no swipe */) {
@Override
public boolean onMove(RecyclerView view, RecyclerView.ViewHolder source,
RecyclerView.ViewHolder target) {
onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
// Swipe is disabled, this is intentionally empty.
}
});
}
public void setRecyclerView(RecyclerView rv) {
mItemTouchHelper.attachToRecyclerView(rv);
}
@Override
public CustomViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
final LocaleDragCell item = (LocaleDragCell) LayoutInflater.from(mContext)
.inflate(R.layout.locale_drag_cell, viewGroup, false);
return new CustomViewHolder(item);
}
@Override
public void onBindViewHolder(final CustomViewHolder holder, int i) {
final LocaleStore.LocaleInfo feedItem = mFeedItemList.get(i);
final LocaleDragCell dragCell = holder.getLocaleDragCell();
String label = feedItem.getFullNameNative();
dragCell.setLabel(label);
dragCell.setLocalized(feedItem.isTranslated());
dragCell.setMiniLabel(Integer.toString(i + 1));
dragCell.setShowCheckbox(mRemoveMode);
dragCell.setShowMiniLabel(!mRemoveMode);
dragCell.setShowHandle(!mRemoveMode);
dragCell.setChecked(false);
dragCell.setTag(feedItem);
dragCell.getCheckbox()
.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
LocaleStore.LocaleInfo feedItem =
(LocaleStore.LocaleInfo) holder.getLocaleDragCell().getTag();
feedItem.setChecked(isChecked);
}
});
}
@Override
public int getItemCount() {
int itemCount = (null != mFeedItemList ? mFeedItemList.size() : 0);
if (itemCount < 2 || mRemoveMode) {
setDragEnabled(false);
} else {
setDragEnabled(true);
}
return itemCount;
}
private void onItemMove(int fromPosition, int toPosition) {
Collections.swap(mFeedItemList, fromPosition, toPosition);
notifyItemChanged(fromPosition); // to update the numbers
notifyItemChanged(toPosition);
notifyItemMoved(fromPosition, toPosition);
}
void setRemoveMode(boolean removeMode) {
mRemoveMode = removeMode;
int itemCount = mFeedItemList.size();
for (int i = 0; i < itemCount; i++) {
mFeedItemList.get(i).setChecked(false);
notifyItemChanged(i);
}
}
void removeChecked() {
int itemCount = mFeedItemList.size();
for (int i = itemCount - 1; i >= 0; i--) {
if (mFeedItemList.get(i).getChecked()) {
mFeedItemList.remove(i);
}
}
notifyDataSetChanged();
doTheUpdate();
}
int getCheckedCount() {
int result = 0;
for (LocaleStore.LocaleInfo li : mFeedItemList) {
if (li.getChecked()) {
result++;
}
}
return result;
}
void addLocale(LocaleStore.LocaleInfo li) {
mFeedItemList.add(li);
notifyItemInserted(mFeedItemList.size() - 1);
doTheUpdate();
}
public void doTheUpdate() {
int count = mFeedItemList.size();
Locale[] newList = new Locale[count];
for (int i = 0; i < count; i++) {
LocaleStore.LocaleInfo li = mFeedItemList.get(i);
newList[i] = li.getLocale();
}
LocaleList ll = new LocaleList(newList);
LocalePicker.updateLocales(ll);
}
private void setDragEnabled(boolean enabled) {
mDragEnabled = enabled;
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2016 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.settings.localepicker;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.settings.R;
class LocaleDragCell extends LinearLayout {
private TextView mLabel;
private TextView mMiniLabel;
private ImageView mLocalized;
private CheckBox mCheckbox;
private ImageView mDragHandle;
public LocaleDragCell(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mLabel = (TextView) findViewById(R.id.label);
mLocalized = (ImageView) findViewById(R.id.l10nWarn);
mMiniLabel = (TextView) findViewById(R.id.miniLabel);
mCheckbox = (CheckBox) findViewById(R.id.checkbox);
mDragHandle = (ImageView) findViewById(R.id.dragHandle);
}
public void setShowHandle(boolean showHandle) {
mDragHandle.setVisibility(showHandle ? VISIBLE : GONE);
invalidate();
requestLayout();
}
public void setShowCheckbox(boolean showCheckbox) {
mCheckbox.setVisibility(showCheckbox ? VISIBLE : GONE);
invalidate();
requestLayout();
}
public void setChecked(boolean checked) {
mCheckbox.setChecked(checked);
}
public void setShowMiniLabel(boolean showMiniLabel) {
mMiniLabel.setVisibility(showMiniLabel ? VISIBLE : GONE);
invalidate();
requestLayout();
}
public void setMiniLabel(String miniLabelText) {
mMiniLabel.setText(miniLabelText);
invalidate();
}
public void setLabel(String labelText) {
mLabel.setText(labelText);
invalidate();
}
public void setLocalized(boolean localized) {
mLocalized.setVisibility(localized ? INVISIBLE : VISIBLE);
invalidate();
}
public ImageView getDragHandle() {
return mDragHandle;
}
public ImageView getTranslateableLabel() {
return mLocalized;
}
public TextView getTextLabel() {
return mLabel;
}
public CheckBox getCheckbox() {
return mCheckbox;
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (C) 2016 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.settings.localepicker;
import android.app.AlertDialog;
import android.app.FragmentTransaction;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.LocaleList;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.android.settings.R;
import com.android.internal.app.LocalePicker;
import com.android.internal.app.LocalePickerWithRegion;
import com.android.internal.app.LocaleStore;
import com.android.settings.InstrumentedFragment;
import com.android.settings.SettingsPreferenceFragment;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Drag-and-drop editor for the user-ordered locale lists.
*/
public class LocaleListEditor extends SettingsPreferenceFragment
implements LocalePickerWithRegion.LocaleSelectedListener {
private static final int MENU_ID_REMOVE = Menu.FIRST + 1;
private LocaleDragAndDropAdapter mAdapter;
private Menu mMenu;
private boolean mRemoveMode;
private View mAddLanguage;
@Override
protected int getMetricsCategory() {
return InstrumentedFragment.USER_LOCALE_LIST;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
LocaleStore.fillCache(this.getContext());
List<LocaleStore.LocaleInfo> feedsList = getUserLocaleList(this.getContext());
mAdapter = new LocaleDragAndDropAdapter(this.getContext(), feedsList);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstState) {
View result = super.onCreateView(inflater, container, savedInstState);
LinearLayout ll = (LinearLayout) result;
View myLayout = inflater.inflate(R.layout.locale_order_list, ll);
getActivity().setTitle(R.string.pref_title_lang_selection);
configureDragAndDrop(myLayout);
return result;
}
@Override
public boolean onOptionsItemSelected(MenuItem menuItem) {
if (menuItem.getItemId() == MENU_ID_REMOVE) {
if (mRemoveMode) {
removeLocaleWarningDialog();
} else {
setRemoveMode(true);
}
return true;
}
return super.onOptionsItemSelected(menuItem);
}
private void setRemoveMode(boolean mRemoveMode) {
this.mRemoveMode = mRemoveMode;
mAdapter.setRemoveMode(mRemoveMode);
mMenu.findItem(MENU_ID_REMOVE).setShowAsAction(
mRemoveMode ? MenuItem.SHOW_AS_ACTION_ALWAYS : MenuItem.SHOW_AS_ACTION_NEVER);
mAddLanguage.setVisibility(mRemoveMode ? View.INVISIBLE : View.VISIBLE);
}
private void removeLocaleWarningDialog() {
int checked = mAdapter.getCheckedCount();
// Nothing checked, just exit remove mode without a warning dialog
if (checked == 0) {
setRemoveMode(!mRemoveMode);
return;
}
// All locales selected, warning dialog, can't remove them all
if (checked == mAdapter.getItemCount()) {
new AlertDialog.Builder(getActivity())
.setTitle(R.string.dlg_remove_locales_error_title)
.setMessage(R.string.dlg_remove_locales_error_message)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
})
.create()
.show();
return;
}
new AlertDialog.Builder(getActivity())
.setTitle(R.string.dlg_remove_locales_title)
.setMessage(R.string.dlg_remove_locales_message)
.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
setRemoveMode(!mRemoveMode);
}
})
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mAdapter.removeChecked();
setRemoveMode(!mRemoveMode);
}
})
.create()
.show();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
final MenuItem menuItem =
menu.add(Menu.NONE, MENU_ID_REMOVE, 0, R.string.locale_remove_menu);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_WITH_TEXT);
menuItem.setIcon(android.R.drawable.ic_menu_delete);
super.onCreateOptionsMenu(menu, inflater);
mMenu = menu;
}
private static List<LocaleStore.LocaleInfo> getUserLocaleList(Context context) {
List<LocaleStore.LocaleInfo> result = new ArrayList<>();
LocaleList localeList = LocalePicker.getLocales();
for (int i = 0; i < localeList.size(); i++) {
Locale locale = localeList.get(i);
result.add(LocaleStore.getLocaleInfo(locale));
}
return result;
}
private void configureDragAndDrop(View view) {
RecyclerView list = (RecyclerView) view.findViewById(R.id.dragList);
list.setLayoutManager(new LinearLayoutManager(this.getContext()));
list.setHasFixedSize(true);
mAdapter.setRecyclerView(list);
list.setAdapter(mAdapter);
mAddLanguage = view.findViewById(R.id.add_language);
mAddLanguage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final LocalePickerWithRegion selector = LocalePickerWithRegion.createLanguagePicker(
getContext(), LocaleListEditor.this, false /* translate only */);
getFragmentManager()
.beginTransaction()
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.replace(getId(), selector)
.addToBackStack("localeListEditor")
.commit();
}
});
}
@Override
public void onLocaleSelected(LocaleStore.LocaleInfo locale) {
mAdapter.addLocale(locale);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2016 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.settings.localepicker;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
class LocaleRecyclerView extends RecyclerView {
public LocaleRecyclerView(Context context) {
super(context);
}
public LocaleRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LocaleRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_UP) {
LocaleDragAndDropAdapter adapter = (LocaleDragAndDropAdapter) this.getAdapter();
if (adapter != null) {
adapter.doTheUpdate();
}
}
return super.onTouchEvent(e);
}
}