Custom ListPreference to choose apps with Images - android

I would like to make a custom ListPreference to let the user choose from his apps. This currently works great. Except:
Filter out system apps (I've filtered by (info.activityInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1 but it still displays system apps)
Icons (Except "check all" item; I guess I need to manipulate the Layout somehow?; Found the solution: http://www.devmil.de/?p=63)
preferences.xml
<com.example.gui.preference.ApplicationListPreference
android:key="pref_key_choose_apps"
android:dependency="pref_key_enable_server"
android:title="#string/choose_apps"
android:dialogTitle="#string/choose_apps"
android:entries="#array/pref_entries_choose_apps"
android:entryValues="#array/pref_values_choose_apps"
example:checkAll="#ALL#" />
arrays.xml
<string-array name="pref_values_choose_apps">
<item>#ALL#</item>
</string-array>
<string-array name="pref_entries_choose_apps">
<item>All</item>
</string-array>
attr.xml
<declare-styleable name="ApplicationListPreference">
<attr format="string" name="checkAll" />
</declare-styleable>
ApplicationListPreference:
public class ApplicationListPreference extends ListPreference {
private final static String SEPARATOR = ";";
private String checkAllKey = null;
private boolean[] mClickedDialogEntryIndices;
public ApplicationListPreference(Context context, AttributeSet attrs) {
super(context, attrs);
checkAllKey = context.obtainStyledAttributes(attrs, R.styleable.ApplicationListPreference).getString(R.styleable.ApplicationListPreference_checkAll);
List<CharSequence> entries = new ArrayList<CharSequence>();
for (CharSequence entry : getEntries()) {
entries.add(entry);
}
List<CharSequence> entryValues = new ArrayList<CharSequence>();
for (CharSequence entryValue : getEntryValues()) {
entryValues.add(entryValue);
}
Intent intentFilter = new Intent(Intent.ACTION_MAIN, null);
intentFilter.addCategory(Intent.CATEGORY_LAUNCHER);
PackageManager pm = context.getPackageManager();
List<ResolveInfo> appList = pm.queryIntentActivities(intentFilter, PackageManager.PERMISSION_GRANTED);
Collections.sort(appList, new ResolveInfo.DisplayNameComparator(pm));
for (ResolveInfo info : appList) {
if ((info.activityInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1) {
entryValues.add(info.activityInfo.packageName);
entries.add(info.loadLabel(pm).toString());
}
}
setEntries(entries.toArray(new CharSequence[entries.size()]));
setEntryValues(entryValues.toArray(new CharSequence[entryValues.size()]));
}
public ApplicationListPreference(Context context) {
this(context, null);
}
#Override
public void setEntries(CharSequence[] entries) {
super.setEntries(entries);
mClickedDialogEntryIndices = new boolean[entries.length];
}
#Override
protected void onPrepareDialogBuilder(Builder builder) {
CharSequence[] entries = getEntries();
CharSequence[] entryValues = getEntryValues();
if (entries == null || entryValues == null || entries.length != entryValues.length) {
throw new IllegalStateException("Irregular array length");
}
restoreCheckedEntries();
builder.setMultiChoiceItems(entries, mClickedDialogEntryIndices, new DialogInterface.OnMultiChoiceClickListener() {
public void onClick(DialogInterface dialog, int which, boolean val) {
if (isCheckAllValue(which) == true) {
checkAll(dialog, val);
}
mClickedDialogEntryIndices[which] = val;
}
});
}
private boolean isCheckAllValue(int which) {
final CharSequence[] entryValues = getEntryValues();
if (checkAllKey != null) {
return entryValues[which].equals(checkAllKey);
}
return false;
}
private void checkAll(DialogInterface dialog, boolean val) {
ListView lv = ((AlertDialog) dialog).getListView();
int size = lv.getCount();
for (int i = 0; i < size; i++) {
lv.setItemChecked(i, val);
mClickedDialogEntryIndices[i] = val;
}
}
public String[] parseStoredValue(CharSequence val) {
if (val == null || "".equals(val)) {
return null;
}
return ((String) val).split(SEPARATOR);
}
private void restoreCheckedEntries() {
CharSequence[] entryValues = getEntryValues();
String[] vals = parseStoredValue(getValue());
if (vals != null) {
List<String> valuesList = Arrays.asList(vals);
for (int i = 0; i < entryValues.length; i++) {
CharSequence entry = entryValues[i];
if (valuesList.contains(entry)) {
mClickedDialogEntryIndices[i] = true;
}
}
}
}
#Override
protected void onDialogClosed(boolean positiveResult) {
ArrayList<String> values = new ArrayList<String>();
CharSequence[] entryValues = getEntryValues();
if (positiveResult && entryValues != null) {
for (int i = 0; i < entryValues.length; i++) {
if (mClickedDialogEntryIndices[i] == true) {
String val = (String) entryValues[i];
if (checkAllKey == null || (val.equals(checkAllKey) == false)) {
values.add(val);
}
}
}
String value = join(values);
if (callChangeListener(value)) {
setValue(value);
}
}
}
protected static String join(Iterable<? extends Object> pColl) {
Iterator< ? extends Object > oIter;
if (pColl == null || (!(oIter = pColl.iterator()).hasNext())) {
return "";
}
StringBuilder oBuilder = new StringBuilder(String.valueOf(oIter.next()));
while (oIter.hasNext()) {
oBuilder.append(SEPARATOR).append(oIter.next());
}
return oBuilder.toString();
}
public static boolean contains(String straw, String haystack) {
for (String val : haystack.split(SEPARATOR)) {
if (val.equals(straw)) {
return true;
}
}
return false;
}
}

For the system apps filtering, you should be checking the flags in the ApplicationInfo instead of ActivityInfo. Try modifying this:
(info.activityInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1
to
(info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 1

Related

Android Some device text watcher not working and some are working

I have working on mention edit text in below code i am extracting text after typing # but this code in working in some device and not in some device.
I have uploaded both video of working and not working in which showing also that which text is getting in text-watcher in red colour in autocomplete list.
This is working video
This is not working video
Any one help me for getting out this problem .This code is working in some devices but in some devices.
public class SocialMentionAutoComplete extends AppCompatMultiAutoCompleteTextView {
UserSearchChatAdapter userSearchChatAdapter;
ArrayMap<String, UserModel> map = new ArrayMap<>();
String formattedOfString = "#%s ";
Context context;
public SocialMentionAutoComplete(Context context) {
super(context);
initializeComponents(context);
}
public SocialMentionAutoComplete(Context context, AttributeSet attrs) {
super(context, attrs);
initializeComponents(context);
}
public SocialMentionAutoComplete(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeComponents(context);
}
private void initializeComponents(Context context) {
this.context = context;
addTextChangedListener(textWatcher);
setOnItemClickListener(onItemSelectedListener);
setTokenizer(new SpaceTokenizer());
}
AdapterView.OnItemClickListener onItemSelectedListener = new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
UserModel mentionPerson = (UserModel) adapterView.getItemAtPosition(i);
map.put("#" + mentionPerson.username, mentionPerson);
}
};
/***
*This function returns the contents of the AppCompatMultiAutoCompleteTextView into my desired Format
*You can write your own function according to your needs
**/
public String getProcessedString() {
String s = getText().toString();
for (Map.Entry<String, UserModel> stringMentionPersonEntry : map.entrySet()) {
s = s.replace(stringMentionPersonEntry.getKey(), stringMentionPersonEntry.getValue().getFormattedValue());
}
return s;
}
/**
* This function will process the incoming text into mention format
* You have to implement the processing logic
*/
public void setMentioningText(String text) {
map.clear();
Pattern p = Pattern.compile("\\[([^]]+)]\\(([^ )]+)\\)");
Matcher m = p.matcher(text);
String finalDesc = text;
while (m.find()) {
UserModel mentionPerson = new UserModel();
String name = m.group(1);
String username = m.group(2);
//Processing Logic
finalDesc = finalDesc.replace("#[" + name + "](" + username + ")", "#" + username);
mentionPerson.name = name;
mentionPerson.username = username;
map.put("#" + username, mentionPerson);
}
int textColor = ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null);
Spannable spannable = new SpannableString(finalDesc);
for (Map.Entry<String, UserModel> stringMentionPersonEntry : map.entrySet()) {
int startIndex = finalDesc.indexOf(stringMentionPersonEntry.getKey());
int endIndex = startIndex + stringMentionPersonEntry.getKey().length();
spannable.setSpan(new ForegroundColorSpan(textColor), startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
setText(spannable);
}
TextWatcher textWatcher = new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
#Override
public void onTextChanged(CharSequence s, int start, int lengthBefore, int lengthAfter) {
try {
if (!s.toString().isEmpty() && s.length() > 1) {//start < s.length()
String name;
if (lengthAfter > lengthBefore) {
name = s.toString().substring(0, start + 1);
} else {
name = s.toString().substring(0, start);
}
if (name.contains("\n")) {
name = name.replaceAll("\n", "");
}
s = name;// this is main passing
int lastTokenIndex = name.lastIndexOf(" #");
int lastIndexOfSpace = name.lastIndexOf(" ");
int nextIndexOfSpace = name.indexOf(" ", start);
if (lastIndexOfSpace > 0 && lastTokenIndex < lastIndexOfSpace) {
String afterString = s.toString().substring(lastIndexOfSpace, s.length());
if (afterString.startsWith(" ") && !afterString.startsWith(" \n")) return;
}
if (lastTokenIndex < 0) {
if (!name.isEmpty() && name.length() >= 1 && name.startsWith("#")) {
lastTokenIndex = 1;
} else
return;
}
int tokenEnd = lastIndexOfSpace;
if (lastIndexOfSpace <= lastTokenIndex) {
tokenEnd = name.length();
if (nextIndexOfSpace != -1 && nextIndexOfSpace < tokenEnd) {
tokenEnd = nextIndexOfSpace;
}
}
if (lastTokenIndex >= 0) {
name = s.toString().substring(lastTokenIndex, tokenEnd).trim();
Pattern pattern = Pattern.compile("^(.+)\\s.+");
Matcher matcher = pattern.matcher(name);
if (!matcher.find()) {
// name = name.replace("#", "").trim();
if (name.equals("#")) {
// getPollsDetail("#");
} else if (!name.isEmpty()) {
getUsers(name);
}
}
}
} else if (!s.toString().isEmpty() && s.length() == 1 && s.toString().equals("#")) {
{
// getPollsDetail("#");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void afterTextChanged(Editable editable) {
}
};
/*
*This function returns results from the web server according to the user name
* I have used Retrofit for Api Communications
* */
public void getUsers(String name) {
AndroidNetworking.cancel("SearchUser");
String url = WebServiceUrl.SEARCH_USER + name.replace("#", "");
new WebTask().nameSearchAPI(context, url, new WebCompleteTaskNew() {
#Override
public void onComplete(String response, int taskcode, String callUrl) {
try {
JSONObject baseResponse = new JSONObject(response);
JSONObject jsonValue = baseResponse.getJSONObject("value");
if (taskcode == RequestCode.CODE_SEARCH_PEOPLE) {
List<UserModel> peopleArrayList = new Gson().fromJson(jsonValue.getJSONArray("data").toString(), new TypeToken<List<UserModel>>() {
}.getType());
userSearchChatAdapter = new UserSearchChatAdapter(getContext(), peopleArrayList, name);
SocialMentionAutoComplete.this.setAdapter(userSearchChatAdapter);
System.out.println("URLL::" + url + "::CLLLLL::" + callUrl);
if (callUrl.equals(url)) {
showDropDown();
}
// SocialMentionAutoComplete.this.showDropDown();
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onError(String message, int taskcode) {
}
});
}
public class SpaceTokenizer implements MultiAutoCompleteTextView.Tokenizer {
public int findTokenStart(CharSequence text, int cursor) {
int i = cursor;
while (i > 0 && text.charAt(i - 1) != ' ') {
i--;
}
while (i < cursor && text.charAt(i) == ' ') {
i++;
}
return i;
}
public int findTokenEnd(CharSequence text, int cursor) {
int i = cursor;
int len = text.length();
while (i < len) {
if (text.charAt(i) == ' ') {
return i;
} else {
i++;
}
}
return len;
}
public CharSequence terminateToken(CharSequence text) {
int i = text.length();
while (i > 0 && text.charAt(i - 1) == ' ') {
i--;
}
if (i > 0 && text.charAt(i - 1) == ' ') {
return text;
} else {
// Returns colored text for selected token
SpannableString sp = new SpannableString(String.format(formattedOfString, text));
int textColor = ResourcesCompat.getColor(getResources(), R.color.colorPrimary, null);
sp.setSpan(new ForegroundColorSpan(textColor), 0, text.length() + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return sp;
}
}
}
}

How do I sort my listview items alphabetically?

I have a listview, which I would like to sort in an alphabetic order. How do I do that?
public class AppDrawer extends AppCompatActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
getSupportActionBar().hide();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_drawer);
ListView userInstalledApps = (ListView) findViewById(R.id.installed_app_list);
List<AppList> installedApps = getInstalledApps();
AppAdapter installedAppAdapter = new AppAdapter(this, installedApps);
userInstalledApps.setAdapter(installedAppAdapter);
}
private List<AppList> getInstalledApps() {
List<AppList> res = new ArrayList<AppList>();
List<PackageInfo> packs = getPackageManager().getInstalledPackages(0);
for (int i = 0; i < packs.size(); i++) {
PackageInfo p = packs.get(i);
if ((isSystemPackage(p) == false)) {
String appName = p.applicationInfo.loadLabel(getPackageManager()).toString();
Drawable icon = p.applicationInfo.loadIcon(getPackageManager());
res.add(new AppList(appName, icon));
}
}
return res;
}
private boolean isSystemPackage(PackageInfo pkgInfo) {
return ((pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ? true : false;
}
You can use comparator for sorting your list. change your getInstalledApps() method as below.
private List<AppList> getInstalledApps() {
List<AppList> res = new ArrayList<AppList>();
List<PackageInfo> packs = getPackageManager().getInstalledPackages(0);
for (int i = 0; i < packs.size(); i++) {
PackageInfo p = packs.get(i);
if ((isSystemPackage(p) == false)) {
String appName = p.applicationInfo.loadLabel(getPackageManager()).toString();
Drawable icon = p.applicationInfo.loadIcon(getPackageManager());
res.add(new AppList(appName, icon));
}
}
Collections.sort(res, new Comparator<AppList>() {
#Override
public int compare(AppList o1, AppList o2) {
return o1.getAppName().compareTo(o2.getAppName()); // use your getter for getting app name you are setting.
}
});
return res;
}
You can use Comparator for sorting a list.
List<AppList> installedApps = getInstalledApps();
Collections.sort(installedApps , new Comparator<AppList>() {
public int compare(AppList v1, AppList v2) {
return v1.getAppName().compareTo(v2.getAppName());
}
});
Or if you are using Java 8:
list.sort(String::compareToIgnoreCase);
sort method of List interface is mutable operation. Your list will be modified and order property of List will be corrupted better to use Streams of Java 8
List.stream().sorted().collect(Collectors.toList());
pass your Comparator in sorted method and get the list.

Masked EditText with format (XXX) XXX-XXXX ext.XXXXXX in android?

How can I mask EditText with below format in android ?
(XXX) XXX-XXXX ext.XXXXXX i.e (654) 321-5846 ext.654321
Used below Custom Edit Text class
package com.test.PhoneNumberFormatter;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v7.widget.AppCompatEditText;
import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.TextView;
import com.test.R;
import static android.content.ContentValues.TAG;
public class MaskedEditText extends AppCompatEditText implements TextWatcher {
public static final String SPACE = " ";
private String mask;
private char charRepresentation;
private boolean keepHint;
private int[] rawToMask;
private RawText rawText;
private boolean editingBefore;
private boolean editingOnChanged;
private boolean editingAfter;
private int[] maskToRaw;
private int selection;
private boolean initialized;
private boolean ignore;
protected int maxRawLength;
private int lastValidMaskPosition;
private boolean selectionChanged;
private OnFocusChangeListener focusChangeListener;
private String allowedChars;
private String deniedChars;
public MaskedEditText(Context context) {
super(context);
init();
}
public MaskedEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.MaskedEditText);
mask = attributes.getString(R.styleable.MaskedEditText_mask);
allowedChars = attributes.getString(R.styleable.MaskedEditText_allowed_chars);
deniedChars = attributes.getString(R.styleable.MaskedEditText_denied_chars);
String representation = attributes.getString(R.styleable.MaskedEditText_char_representation);
if (representation == null) {
charRepresentation = '#';
} else {
charRepresentation = representation.charAt(0);
}
keepHint = attributes.getBoolean(R.styleable.MaskedEditText_keep_hint, false);
cleanUp();
// Ignoring enter key presses
setOnEditorActionListener(new OnEditorActionListener() {
#Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
switch (actionId) {
// case EditorInfo.IME_ACTION_NEXT:
// fixing actionNext
// return false;
default:
return true;
}
}
});
attributes.recycle();
}
#Override
public Parcelable onSaveInstanceState() {
final Parcelable superParcellable = super.onSaveInstanceState();
final Bundle state = new Bundle();
state.putParcelable("super", superParcellable);
state.putString("text", getRawText());
state.putBoolean("keepHint", isKeepHint());
return state;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle) state;
keepHint = bundle.getBoolean("keepHint", false);
super.onRestoreInstanceState(((Bundle) state).getParcelable("super"));
final String text = bundle.getString("text");
setText(text);
Log.d(TAG, "onRestoreInstanceState: " + text);
}
#Override
public void setText(CharSequence text, BufferType type) {
// if (text == null || text.equals("")) return;
super.setText(text, type);
}
/**
* #param listener - its onFocusChange() method will be called before performing MaskedEditText operations,
* related to this event.
*/
#Override
public void setOnFocusChangeListener(OnFocusChangeListener listener) {
focusChangeListener = listener;
}
private void cleanUp() {
initialized = false;
generatePositionArrays();
rawText = new RawText();
selection = rawToMask[0];
editingBefore = true;
editingOnChanged = true;
editingAfter = true;
if (hasHint() && rawText.length() == 0) {
this.setText(makeMaskedTextWithHint());
} else {
this.setText(makeMaskedText());
}
editingBefore = false;
editingOnChanged = false;
editingAfter = false;
maxRawLength = maskToRaw[previousValidPosition(mask.length() - 1)] + 1;
lastValidMaskPosition = findLastValidMaskPosition();
initialized = true;
super.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (focusChangeListener != null) {
focusChangeListener.onFocusChange(v, hasFocus);
}
if (hasFocus()) {
selectionChanged = false;
MaskedEditText.this.setSelection(lastValidPosition());
}
}
});
}
private int findLastValidMaskPosition() {
for (int i = maskToRaw.length - 1; i >= 0; i--) {
if (maskToRaw[i] != -1) return i;
}
throw new RuntimeException("Mask must contain at least one representation char");
}
private boolean hasHint() {
return getHint() != null;
}
public MaskedEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void setMask(String mask) {
this.mask = mask;
cleanUp();
}
public String getMask() {
return this.mask;
}
public String getRawText() {
return this.rawText.getText();
}
public void setCharRepresentation(char charRepresentation) {
this.charRepresentation = charRepresentation;
cleanUp();
}
public char getCharRepresentation() {
return this.charRepresentation;
}
/**
* Generates positions for values characters. For instance:
* Input data: mask = "+7(###)###-##-##
* After method execution:
* rawToMask = [3, 4, 5, 6, 8, 9, 11, 12, 14, 15]
* maskToRaw = [-1, -1, -1, 0, 1, 2, -1, 3, 4, 5, -1, 6, 7, -1, 8, 9]
* charsInMask = "+7()- " (and space, yes)
*/
private void generatePositionArrays() {
int[] aux = new int[mask.length()];
maskToRaw = new int[mask.length()];
String charsInMaskAux = "";
int charIndex = 0;
for (int i = 0; i < mask.length(); i++) {
char currentChar = mask.charAt(i);
if (currentChar == charRepresentation) {
aux[charIndex] = i;
maskToRaw[i] = charIndex++;
} else {
String charAsString = Character.toString(currentChar);
if (!charsInMaskAux.contains(charAsString)) {
charsInMaskAux = charsInMaskAux.concat(charAsString);
}
maskToRaw[i] = -1;
}
}
if (charsInMaskAux.indexOf(' ') < 0) {
charsInMaskAux = charsInMaskAux + SPACE;
}
char[] charsInMask = charsInMaskAux.toCharArray();
rawToMask = new int[charIndex];
for (int i = 0; i < charIndex; i++) {
rawToMask[i] = aux[i];
}
}
private void init() {
addTextChangedListener(this);
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
if (!editingBefore) {
editingBefore = true;
if (start > lastValidMaskPosition) {
ignore = true;
}
int rangeStart = start;
if (after == 0) {
rangeStart = erasingStart(start);
}
Range range = calculateRange(rangeStart, start + count);
if (range.getStart() != -1) {
rawText.subtractFromString(range);
}
if (count > 0) {
selection = previousValidPosition(start);
}
}
}
private int erasingStart(int start) {
while (start > 0 && maskToRaw[start] == -1) {
start--;
}
return start;
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (!editingOnChanged && editingBefore) {
editingOnChanged = true;
if (ignore) {
return;
}
if (count > 0) {
int startingPosition = maskToRaw[nextValidPosition(start)];
String addedString = s.subSequence(start, start + count).toString();
count = rawText.addToString(clear(addedString), startingPosition, maxRawLength);
if (initialized) {
int currentPosition;
if (startingPosition + count < rawToMask.length)
currentPosition = rawToMask[startingPosition + count];
else
currentPosition = lastValidMaskPosition + 1;
selection = nextValidPosition(currentPosition);
}
}
}
}
#Override
public void afterTextChanged(Editable s) {
if (!editingAfter && editingBefore && editingOnChanged) {
editingAfter = true;
if (hasHint() && (keepHint || rawText.length() == 0)) {
setText(makeMaskedTextWithHint());
} else {
setText(makeMaskedText());
}
selectionChanged = false;
setSelection(selection);
editingBefore = false;
editingOnChanged = false;
editingAfter = false;
ignore = false;
}
}
public boolean isKeepHint() {
return keepHint;
}
public void setKeepHint(boolean keepHint) {
this.keepHint = keepHint;
setText(getRawText());
}
#Override
protected void onSelectionChanged(int selStart, int selEnd) {
// On Android 4+ this method is being called more than 1 time if there is a hint in the EditText, what moves the cursor to left
// Using the boolean var selectionChanged to limit to one execution
if (initialized) {
if (!selectionChanged) {
selStart = fixSelection(selStart);
selEnd = fixSelection(selEnd);
// exactly in this order. If getText.length() == 0 then selStart will be -1
if (selStart > getText().length()) selStart = getText().length();
if (selStart < 0) selStart = 0;
// exactly in this order. If getText.length() == 0 then selEnd will be -1
if (selEnd > getText().length()) selEnd = getText().length();
if (selEnd < 0) selEnd = 0;
setSelection(selStart, selEnd);
selectionChanged = true;
} else {
//check to see if the current selection is outside the already entered text
if (selStart > rawText.length() - 1) {
final int start = fixSelection(selStart);
final int end = fixSelection(selEnd);
if (start >= 0 && end < getText().length()) {
setSelection(start, end);
}
}
}
}
super.onSelectionChanged(selStart, selEnd);
}
private int fixSelection(int selection) {
if (selection > lastValidPosition()) {
return lastValidPosition();
} else {
return nextValidPosition(selection);
}
}
private int nextValidPosition(int currentPosition) {
while (currentPosition < lastValidMaskPosition && maskToRaw[currentPosition] == -1) {
currentPosition++;
}
if (currentPosition > lastValidMaskPosition) return lastValidMaskPosition + 1;
return currentPosition;
}
private int previousValidPosition(int currentPosition) {
while (currentPosition >= 0 && maskToRaw[currentPosition] == -1) {
currentPosition--;
if (currentPosition < 0) {
return nextValidPosition(0);
}
}
return currentPosition;
}
private int lastValidPosition() {
if (rawText.length() == maxRawLength) {
return rawToMask[rawText.length() - 1] + 1;
}
return nextValidPosition(rawToMask[rawText.length()]);
}
private String makeMaskedText() {
int maskedTextLength;
if (rawText.length() < rawToMask.length) {
maskedTextLength = rawToMask[rawText.length()];
} else {
maskedTextLength = mask.length();
}
char[] maskedText = new char[maskedTextLength]; //mask.replace(charRepresentation, ' ').toCharArray();
for (int i = 0; i < maskedText.length; i++) {
int rawIndex = maskToRaw[i];
if (rawIndex == -1) {
maskedText[i] = mask.charAt(i);
} else {
maskedText[i] = rawText.charAt(rawIndex);
}
}
return new String(maskedText);
}
private CharSequence makeMaskedTextWithHint() {
SpannableStringBuilder ssb = new SpannableStringBuilder();
int mtrv;
int maskFirstChunkEnd = rawToMask[0];
for (int i = 0; i < mask.length(); i++) {
mtrv = maskToRaw[i];
if (mtrv != -1) {
if (mtrv < rawText.length()) {
ssb.append(rawText.charAt(mtrv));
} else {
ssb.append(getHint().charAt(maskToRaw[i]));
}
} else {
ssb.append(mask.charAt(i));
}
if ((keepHint && rawText.length() < rawToMask.length && i >= rawToMask[rawText.length()])
|| (!keepHint && i >= maskFirstChunkEnd)) {
ssb.setSpan(new ForegroundColorSpan(getCurrentHintTextColor()), i, i + 1, 0);
}
}
return ssb;
}
private Range calculateRange(int start, int end) {
Range range = new Range();
for (int i = start; i <= end && i < mask.length(); i++) {
if (maskToRaw[i] != -1) {
if (range.getStart() == -1) {
range.setStart(maskToRaw[i]);
}
range.setEnd(maskToRaw[i]);
}
}
if (end == mask.length()) {
range.setEnd(rawText.length());
}
if (range.getStart() == range.getEnd() && start < end) {
int newStart = previousValidPosition(range.getStart() - 1);
if (newStart < range.getStart()) {
range.setStart(newStart);
}
}
return range;
}
private String clear(String string) {
if (deniedChars != null) {
for (char c : deniedChars.toCharArray()) {
string = string.replace(Character.toString(c), "");
}
}
if (allowedChars != null) {
StringBuilder builder = new StringBuilder(string.length());
for (char c : string.toCharArray()) {
if (allowedChars.contains(String.valueOf(c))) {
builder.append(c);
}
}
string = builder.toString();
}
return string;
}
}
In activity_main.xml
<com.test.PhoneNumberFormatter.MaskedEditText
android:id="#+id/phone_input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:hint="#string/phone_number"
android:imeOptions="actionNext"
android:inputType="textPhonetic"
android:paddingBottom="#dimen/_3sdp"
android:paddingTop="#dimen/_8sdp"
android:textColorHint="#color/grey_color"
android:textSize="#dimen/_12sdp"
mask:allowed_chars="1234567890ext."
mask:keep_hint="false"
mask:mask="(###)###-#### ext.######" />
Thanks.
For format Edit-text, below things can be helpful to you.
**Step 1** : Add following line in your build.gradle file
implementation 'com.github.pinball83:masked-edittext:1.0.4'
**Step 2 :** by xml :
<com.github.pinball83.maskededittext.MaskedEditText
android:id="#+id/masked_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="number"
app:mask="8 (***) *** **-**"
app:notMaskedSymbol="*"
app:maskIcon="#drawable/abc_ic_clear_mtrl_alpha"
app:maskIconColor="#color/colorPrimary"
/>
or by java file :
MaskedEditText maskedEditText = new MaskedEditText.Builder(context)
.mask("8 (***) *** **-**")
.notMaskedSymbol("*")
.icon(R.drawable.ic_account_circle)
.iconCallback(unmaskedText -> { //Icon click callback handler })
.build();
MaskedEditText editText = new MaskedEditText.Builder(context)
.mask("8 (***) *** **-**")
.notMaskedSymbol("*")
.build();; //set mask to "8 (***) *** **-**" and not masked symbol to "*"
Text setup and formatting
MaskedEditText editText = new MaskedEditText..Builder(context)
.mask("8 (***) *** **-**")
.notMaskedSymbol("*")
.format("[1][2][3] [4][5][6]-[7][8]-[10][9]")//set format of returned data input into MaskedEditText
.build();
editText.setMaskedText("5551235567");
for more details use following url :
https://github.com/pinball83/Masked-Edittext
Enjoy :)
Step 1 : Create editText
<EditText
android:id="#+id/masked_edit_text"
android:layout_width="yourSize"
android:layout_height="yourSize"
android:inputType="textPassword"/>
Step2:
public class CustomTransformation extends PasswordTransformationMethod {
#Override
public CharSequence getTransformation(CharSequence source, View view) {
return new TransformedSequence(source);
}
private class TransformedSequence implements CharSequence {
private CharSequence sequence;
public TransformedSequence(CharSequence source) {
sequence = source; // Store char sequence
}
public char charAt(int index) {
return 'X'; // Replace with what you want.
}
public int length() {
return sequence.length(); // return the length
}
public CharSequence subSequence(int start, int end) {
return sequence.subSequence(start, end); //return sequence
}
}
Step 3: apply the transformation
EditText edittext = findViewById(R.id.masked_edit_text);
edittext.setTransformationMethod(new CustomTransformation());
https://github.com/mukeshsolanki/country-picker-android
try this library .
this will automatically mask your edittext with selected country number .

MultiSelectListPreference onPreferenceChange() method getting wrong parameters

I have an Android app with a MultiSelectListPreference, and I'm using the onPreferenceChange() method to update the Preference's summary. I've managed to write the code that updates the summary based on the newValues parameter, but the contents of the Object do not match the actual options selected by the user.
Here is my code:
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference instanceof MultiSelectListPreference) {
List<String> newValues = new ArrayList<>((HashSet<String>) newValue);
MultiSelectListPreference pref = (MultiSelectListPreference) preference;
ArrayList<String> newSummary = new ArrayList<>();
ArrayList<CharSequence> values = new ArrayList<>(Arrays.asList(pref.getEntryValues()));
for (int i = 0; i < newValues.size(); i++) {
int currentIndex = findIndexOfString(values, newValues.get(i).replaceAll(" ", ""));
String title = (currentIndex >= 0) ? pref.getEntries()[currentIndex].toString().replaceAll(" ", "") : "";
newSummary.add(title);
}
pref.setSummary(TextUtils.join(", ", newSummary));
}
return true;
}
private static int findIndexOfString(List<CharSequence> list, String s) {
for (int i = 0; i < list.size(); i++) {
if (s.equals(list.get(i).toString().replaceAll(" ", ""))) {
return i;
}
}
return -1;
}
This is the code I'm using to set summary based on the newValue Object received from onPreferenceChange(), which contains the values stored as a preference. (Not good for the summary)
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (preference instanceof MultiSelectListPreference) {
List<String> newValues = new ArrayList<>((HashSet<String>) newValue);
pref.setSummary(TextUtils.join(", ", getSummaryListFromValueList(newValues)));
}
return true;
}
private List<String> getSummaryListFromValueList(List<String> valueList) {
String[] allSummaries = getResources().getStringArray(R.array.pref_notif);
String[] allValues = getResources().getStringArray(R.array.pref_notif_values);
List<String> summaryList = new ArrayList<>();
for (int i = 0; i < allValues.length; i++) {
for (String value : valueList) {
if (allValues[i].equals(value)) {
summaryList.add(allSummaries[i]);
}
}
}
return summaryList;
}

Dynamic playlists with Exoplayer 2

I'd like to use ExoPlayer2 with playlists having possibility to dinamically change the tracks (add or remove them from playlist) and change the loop settings.
Since ConcatenatingMediaSource has static arrays (and not lists), I'm implementing a DynamicMediaSource, like Concatenating one but with lists instead of arrays and one mode method addSource to add one more media source to the list.
public void addSource(MediaSource mediaSource) {
this.mediaSources.add(mediaSource);
duplicateFlags = buildDuplicateFlags(this.mediaSources);
if(!mediaSources.isEmpty())
prepareSource(mediaSources.size() -1);
else
prepareSource(0);
}
When I invoke addSource
MediaSource ms = buildMediaSource(mynewuri, null);
mediaSource.addSource(ms);
the track is added to the arrays but it seems something is missing because I always obtain ArrayOutOfBoundsException in createPeriod method.
In createPeriod the method
mediaSources.get(sourceIndex)...
is trying to access the index = mediaSources.size().
Can you help me?
I eventually managed it.
It was my fault during the conversion from arrays to lists.
I had to use SparseArrays for timelines and manifests and everything began to work.
In the DynamicMediaSource simply set the following types:
private final List<MediaSource> mediaSources;
private final SparseArray<Timeline> timelines;
private final SparseArray<Object> manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private SparseArray<Boolean> duplicateFlags;
you have to use sparse arrays to set the proper values into the timelines and manifests in the method
private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
Object sourceManifest) {
// Set the timeline and manifest.
timelines.put(sourceFirstIndex, sourceTimeline);
manifests.put(sourceFirstIndex, sourceManifest);
// Also set the timeline and manifest for any duplicate entries of the same source.
for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
timelines.put(i, sourceTimeline);
manifests.put(i, sourceManifest);
}
}
for(int i= 0; i<mediaSources.size(); i++){
if(timelines.get(i) == null){
// Don't invoke the listener until all sources have timelines.
return;
}
}
timeline = new DynamicTimeline(new ArrayList(asList(timelines)));
listener.onSourceInfoRefreshed(timeline, new ArrayList(asList(manifests)));
}
Here is the complete code of DynamicMediaSource class:
public final class DynamicMediaSource implements MediaSource {
private static final String TAG = "DynamicSource";
private final List<MediaSource> mediaSources;
private final List<Timeline> timelines;
private final List<Object> manifests;
private final Map<MediaPeriod, Integer> sourceIndexByMediaPeriod;
private SparseArray<Boolean> duplicateFlags;
private Listener listener;
private DynamicTimeline timeline;
/**
* #param mediaSources The {#link MediaSource}s to concatenate. It is valid for the same
* {#link MediaSource} instance to be present more than once in the array.
*/
public DynamicMediaSource(MediaSource... mediaSources) {
this.mediaSources = new ArrayList<MediaSource>(Arrays.asList(mediaSources));
timelines = new ArrayList<Timeline>();
manifests = new ArrayList<Object>();
sourceIndexByMediaPeriod = new HashMap<>();
duplicateFlags = buildDuplicateFlags(this.mediaSources);
}
public void addSource(MediaSource mediaSource) {
this.mediaSources.add(mediaSource);
duplicateFlags = buildDuplicateFlags(this.mediaSources);
/*if(!mediaSources.isEmpty())
prepareSource(mediaSources.size() -1);
else
prepareSource(0);*/
}
#Override
public void prepareSource(Listener listener) {
this.listener = listener;
for (int i = 0; i < mediaSources.size(); i++) {
prepareSource(i);
/*if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
final int index = i;
mediaSources.get(i).prepareSource(new Listener() {
#Override
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
handleSourceInfoRefreshed(index, timeline, manifest);
}
});
}*/
}
}
private void prepareSource(int sourceindex) {
if (duplicateFlags.get(sourceindex) == null || !duplicateFlags.get(sourceindex)) {
final int index = sourceindex;
mediaSources.get(sourceindex).prepareSource(new Listener() {
#Override
public void onSourceInfoRefreshed(Timeline timeline, Object manifest) {
handleSourceInfoRefreshed(index, timeline, manifest);
}
});
}
}
#Override
public void maybeThrowSourceInfoRefreshError() throws IOException {
for (int i = 0; i < mediaSources.size(); i++) {
if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
mediaSources.get(i).maybeThrowSourceInfoRefreshError();
}
}
}
#Override
public MediaPeriod createPeriod(int index, Callback callback, Allocator allocator,
long positionUs) {
int sourceIndex = timeline.getSourceIndexForPeriod(index);
int periodIndexInSource = index - timeline.getFirstPeriodIndexInSource(sourceIndex);
MediaPeriod mediaPeriod = mediaSources.get(sourceIndex).createPeriod(periodIndexInSource, callback,
allocator, positionUs);
sourceIndexByMediaPeriod.put(mediaPeriod, sourceIndex);
return mediaPeriod;
}
#Override
public void releasePeriod(MediaPeriod mediaPeriod) {
int sourceIndex = sourceIndexByMediaPeriod.get(mediaPeriod);
sourceIndexByMediaPeriod.remove(mediaPeriod);
mediaSources.get(sourceIndex).releasePeriod(mediaPeriod);
}
#Override
public void releaseSource() {
for (int i = 0; i < mediaSources.size(); i++) {
if (duplicateFlags.get(i) == null || !duplicateFlags.get(i)) {
mediaSources.get(i).releaseSource();
}
}
}
private void handleSourceInfoRefreshed(int sourceFirstIndex, Timeline sourceTimeline,
Object sourceManifest) {
// Set the timeline and manifest.
timelines.add(sourceFirstIndex, sourceTimeline);
manifests.add(sourceFirstIndex, sourceManifest);
// Also set the timeline and manifest for any duplicate entries of the same source.
for (int i = sourceFirstIndex + 1; i < mediaSources.size(); i++) {
if (mediaSources.get(i).equals(mediaSources.get(sourceFirstIndex))) {
timelines.add(i, sourceTimeline);
manifests.add(i, sourceManifest);
}
}
for (Timeline timeline : timelines) {
if (timeline == null) {
// Don't invoke the listener until all sources have timelines.
return;
}
}
timeline = new DynamicTimeline(new ArrayList(timelines));
listener.onSourceInfoRefreshed(timeline, new ArrayList(manifests));
}
private static SparseArray<Boolean> buildDuplicateFlags(List<MediaSource> mediaSources) {
SparseArray<Boolean> duplicateFlags = new SparseArray<Boolean>();
IdentityHashMap<MediaSource, Void> sources = new IdentityHashMap<>(mediaSources.size());
for (int i = 0; i < mediaSources.size(); i++) {
MediaSource mediaSource = mediaSources.get(i);
if (!sources.containsKey(mediaSource)) {
sources.put(mediaSource, null);
} else {
duplicateFlags.setValueAt(i, true);
}
}
return duplicateFlags;
}
/**
* A {#link Timeline} that is the concatenation of one or more {#link Timeline}s.
*/
private static final class DynamicTimeline extends Timeline {
private final List<Timeline> timelines;
private final List<Integer> sourcePeriodOffsets;
private final List<Integer> sourceWindowOffsets;
public DynamicTimeline(List<Timeline> timelines) {
List<Integer> sourcePeriodOffsets = new ArrayList<>();
List<Integer> sourceWindowOffsets = new ArrayList<>();
int periodCount = 0;
int windowCount = 0;
for (Timeline timeline : timelines) {
periodCount += timeline.getPeriodCount();
windowCount += timeline.getWindowCount();
sourcePeriodOffsets.add(periodCount);
sourceWindowOffsets.add(windowCount);
}
this.timelines = timelines;
this.sourcePeriodOffsets = sourcePeriodOffsets;
this.sourceWindowOffsets = sourceWindowOffsets;
}
#Override
public int getWindowCount() {
return sourceWindowOffsets.get(sourceWindowOffsets.size() - 1);
}
#Override
public Window getWindow(int windowIndex, Window window, boolean setIds) {
int sourceIndex = getSourceIndexForWindow(windowIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
timelines.get(sourceIndex).getWindow(windowIndex - firstWindowIndexInSource, window, setIds);
window.firstPeriodIndex += firstPeriodIndexInSource;
window.lastPeriodIndex += firstPeriodIndexInSource;
return window;
}
#Override
public int getPeriodCount() {
return sourcePeriodOffsets.get(sourcePeriodOffsets.size() - 1);
}
#Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
int sourceIndex = getSourceIndexForPeriod(periodIndex);
int firstWindowIndexInSource = getFirstWindowIndexInSource(sourceIndex);
int firstPeriodIndexInSource = getFirstPeriodIndexInSource(sourceIndex);
timelines.get(sourceIndex).getPeriod(periodIndex - firstPeriodIndexInSource, period, setIds);
period.windowIndex += firstWindowIndexInSource;
if (setIds) {
period.uid = Pair.create(sourceIndex, period.uid);
}
return period;
}
#Override
public int getIndexOfPeriod(Object uid) {
if (!(uid instanceof Pair)) {
return C.INDEX_UNSET;
}
Pair<?, ?> sourceIndexAndPeriodId = (Pair<?, ?>) uid;
if (!(sourceIndexAndPeriodId.first instanceof Integer)) {
return C.INDEX_UNSET;
}
int sourceIndex = (Integer) sourceIndexAndPeriodId.first;
Object periodId = sourceIndexAndPeriodId.second;
if (sourceIndex < 0 || sourceIndex >= timelines.size()) {
return C.INDEX_UNSET;
}
int periodIndexInSource = timelines.get(sourceIndex).getIndexOfPeriod(periodId);
return periodIndexInSource == C.INDEX_UNSET ? C.INDEX_UNSET
: getFirstPeriodIndexInSource(sourceIndex) + periodIndexInSource;
}
private int getSourceIndexForPeriod(int periodIndex) {
return Util.binarySearchFloor(sourcePeriodOffsets, periodIndex, true, false) + 1;
}
private int getFirstPeriodIndexInSource(int sourceIndex) {
return sourceIndex == 0 ? 0 : sourcePeriodOffsets.get(sourceIndex - 1);
}
private int getSourceIndexForWindow(int windowIndex) {
return Util.binarySearchFloor(sourceWindowOffsets, windowIndex, true, false) + 1;
}
private int getFirstWindowIndexInSource(int sourceIndex) {
return sourceIndex == 0 ? 0 : sourceWindowOffsets.get(sourceIndex - 1);
}
}
}

Categories

Resources