diff --git a/build.gradle b/build.gradle index 5fdca31..a4b686e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,21 +1,35 @@ buildscript { + ext.kotlin_version = '1.3.72' repositories { - mavenCentral() + google() + jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.1.2' + + classpath 'com.android.tools.build:gradle:3.5.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +allprojects { + repositories { + google() + jcenter() + } } apply plugin: 'com.android.library' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-android' android { - compileSdkVersion 22 - buildToolsVersion "22.0.1" + compileSdkVersion 28 + buildToolsVersion "29.0.3" defaultConfig { - minSdkVersion 10 - targetSdkVersion 22 + minSdkVersion 21 + targetSdkVersion 28 versionCode 106 versionName "1.0.6" } @@ -28,6 +42,16 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - compile 'com.android.support:appcompat-v7:22.2.1' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'androidx.appcompat:appcompat:1.0.2' + + // Recyclerview + def recyclerview_version = "1.1.0-beta03" + implementation "androidx.recyclerview:recyclerview:$recyclerview_version" + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation "androidx.core:core-ktx:+" + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" +} +repositories { + mavenCentral() } diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..5465fec --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6c4ea01..f5658cb 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..e7b4def --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':app' diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index 84fff78..4472fb4 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -1,10 +1,16 @@ + package="com.heetch.countrypicker"> - + + + + + + + + - + \ No newline at end of file diff --git a/src/main/java/com/heetch/countrypicker/Country.java b/src/main/java/com/heetch/countrypicker/Country.java index af06b56..45a7d29 100644 --- a/src/main/java/com/heetch/countrypicker/Country.java +++ b/src/main/java/com/heetch/countrypicker/Country.java @@ -6,20 +6,22 @@ public class Country { private String isoCode; private String dialingCode; + private String countryName; - public Country() { - - } - - public Country(String isoCode, String dialingCode) { + public Country(String name, String isoCode, String dialingCode) { this.isoCode = isoCode; this.dialingCode = dialingCode; + this.countryName = name; } public String getIsoCode() { return isoCode; } + public String getCountryName() { + return countryName; + } + public void setIsoCode(String isoCode) { this.isoCode = isoCode; } diff --git a/src/main/java/com/heetch/countrypicker/CountryListAdapter.java b/src/main/java/com/heetch/countrypicker/CountryListAdapter.java index cc93e17..75801cf 100644 --- a/src/main/java/com/heetch/countrypicker/CountryListAdapter.java +++ b/src/main/java/com/heetch/countrypicker/CountryListAdapter.java @@ -5,89 +5,127 @@ import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; +import android.widget.Filter; +import android.widget.Filterable; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; import java.util.List; import java.util.Locale; /** * Created by GODARD Tuatini on 07/05/15. */ -public class CountryListAdapter extends BaseAdapter { +public class CountryListAdapter extends RecyclerView.Adapter implements Filterable { private final Context mContext; private static final String TAG = CountryListAdapter.class.getSimpleName(); - private LayoutInflater inflater; - private List countries; + private ArrayList countries; private boolean showDialingCode; + public OnItemClickListener onItemClickListener; + private List countryListFiltered; - public CountryListAdapter(Context context, List countries, boolean showDialingCode) { + public CountryListAdapter(Context context, List countries, boolean showDialingCode, + OnItemClickListener onItemClickListener) { mContext = context; - this.countries = countries; + this.countries = (ArrayList) countries; + countryListFiltered = countries; this.showDialingCode = showDialingCode; - inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + this.onItemClickListener = onItemClickListener; } - @Override - public int getCount() { - return countries.size(); - } + + @NonNull @Override - public Object getItem(int position) { - return countries.get(position); + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + View view = inflater.inflate(R.layout.item_country, parent, false); + return new ViewHolder(view); } @Override - public long getItemId(int position) { - return 0; + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + Country country = countryListFiltered.get(position); + holder.name.setText(country.getCountryName()); + String drawableName = country.getIsoCode().toLowerCase(Locale.ENGLISH) + "_flag"; + holder.icon.setImageResource(Utils.getMipmapResId(holder.icon.getContext(), drawableName)); } @Override - public View getView(int position, View convertView, ViewGroup parent) { - View itemView = convertView; - Item item; - Country country = countries.get(position); - - if (convertView == null) { - item = new Item(); - itemView = inflater.inflate(R.layout.item_country, parent, false); - item.setIcon((ImageView) itemView.findViewById(R.id.icon)); - item.setName((TextView) itemView.findViewById(R.id.name)); - itemView.setTag(item); - } else { - item = (Item) itemView.getTag(); - } + public int getItemCount() { + return countryListFiltered.size(); + } - item.getName().setText(new Locale(mContext.getResources().getConfiguration().locale.getLanguage(), - country.getIsoCode()).getDisplayCountry() + (showDialingCode ? - " (+" + country.getDialingCode() + ")" : "")); + public void update(List filterList) { + countries.clear(); + countries.addAll(filterList); + notifyDataSetChanged(); - // Load drawable dynamically from country code - String drawableName = country.getIsoCode().toLowerCase(Locale.ENGLISH) + "_flag"; - item.getIcon().setImageResource(Utils.getMipmapResId(mContext, drawableName)); - return itemView; } - public static class Item { - private TextView name; - private ImageView icon; + class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { - public ImageView getIcon() { - return icon; - } + TextView name; + ImageView icon; - public void setIcon(ImageView icon) { - this.icon = icon; + public ViewHolder(@NonNull View itemView) { + super(itemView); + icon = itemView.findViewById(R.id.icon); + name = itemView.findViewById(R.id.name); + itemView.setOnClickListener(this); } - public TextView getName() { - return name; + @Override + public void onClick(View v) { + onItemClickListener.onItemClick(getAdapterPosition(), countryListFiltered.get(getAdapterPosition())); } + } - public void setName(TextView name) { - this.name = name; - } + interface OnItemClickListener{ + void onItemClick(int position, Country country); + } + + @Override + public Filter getFilter() { + return new Filter() { + @Override + protected FilterResults performFiltering(CharSequence charSequence) { + String charString = charSequence.toString(); + if (charString.isEmpty()) { + countryListFiltered = countries; + } else { + List filteredList = new ArrayList<>(); + for (Country row : countries) { + + // name match condition. this might differ depending on your requirement + // here we are looking for name or phone number match + if (row.getCountryName().toLowerCase() + .contains(charString.toLowerCase())) { + filteredList.add(row); + } + } + + countryListFiltered = filteredList; + } + + FilterResults filterResults = new FilterResults(); + filterResults.values = countryListFiltered; + return filterResults; + } + + @Override + protected void publishResults(CharSequence charSequence, FilterResults filterResults) { + countryListFiltered = (ArrayList) filterResults.values; + + // refresh the list with filtered data + notifyDataSetChanged(); + } + }; } } diff --git a/src/main/java/com/heetch/countrypicker/CountryPickerDialog.java b/src/main/java/com/heetch/countrypicker/CountryPickerDialog.java index ddcddb2..7ed11b1 100644 --- a/src/main/java/com/heetch/countrypicker/CountryPickerDialog.java +++ b/src/main/java/com/heetch/countrypicker/CountryPickerDialog.java @@ -2,12 +2,13 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.view.ViewCompat; -import android.support.v7.app.AppCompatDialog; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ListView; +import android.view.Window; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatDialog; +import androidx.appcompat.widget.SearchView; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import java.text.Collator; import java.util.Collections; @@ -22,9 +23,11 @@ public class CountryPickerDialog extends AppCompatDialog { private List countries; private CountryPickerCallbacks callbacks; - private ListView listview; + private RecyclerView recyclerView; private String headingCountryCode; private boolean showDialingCode; + private CountryListAdapter adapter; + private SearchView search_et; public CountryPickerDialog(Context context, CountryPickerCallbacks callbacks) { this(context, callbacks, null, true); @@ -48,7 +51,7 @@ public CountryPickerDialog(Context context, CountryPickerCallbacks callbacks, this.callbacks = callbacks; this.headingCountryCode = headingCountryCode; this.showDialingCode = showDialingCode; - countries = Utils.parseCountries(Utils.getCountriesJSON(this.getContext())); + countries = Utils.parseCountries(context, Utils.getCountriesJSON(this.getContext())); Collections.sort(countries, new Comparator() { @Override public int compare(Country country1, Country country2) { @@ -65,35 +68,56 @@ public int compare(Country country1, Country country2) { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + supportRequestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.country_picker); - ViewCompat.setElevation(getWindow().getDecorView(), 3); - listview = (ListView) findViewById(R.id.country_picker_listview); + recyclerView = findViewById(R.id.country_picker_listview); + + search_et = findViewById(R.id.country_search); + + search_et.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } - CountryListAdapter adapter = new CountryListAdapter(this.getContext(), countries, showDialingCode); - listview.setAdapter(adapter); - listview.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { + public boolean onQueryTextChange(String newText) { + adapter.getFilter().filter(newText); + return false; + } + }); + + LinearLayoutManager manager = new LinearLayoutManager(getContext()); + recyclerView.setLayoutManager(manager); + + adapter = new CountryListAdapter(this.getContext(), countries, + showDialingCode, new CountryListAdapter.OnItemClickListener() { + @Override + public void onItemClick(int position, Country country) { hide(); - Country country = countries.get(position); callbacks.onCountrySelected(country, Utils.getMipmapResId(getContext(), country.getIsoCode().toLowerCase(Locale.ENGLISH) + "_flag")); } }); + recyclerView.setAdapter(adapter); + + - scrollToHeadingCountry(); + + + //scrollToHeadingCountry(); } - private void scrollToHeadingCountry() { + /*private void scrollToHeadingCountry() { if (headingCountryCode != null) { - for (int i = 0; i < listview.getCount(); i++) { - if (((Country) listview.getItemAtPosition(i)).getIsoCode().toLowerCase() + for (int i = 0; i < adapter.getItemCount(); i++) { + if (((Country) recyclerView.getItemAtPosition(i)).getIsoCode().toLowerCase() .equals(headingCountryCode.toLowerCase())) { - listview.setSelection(i); + recyclerView.setSelection(i); } } } - } + }*/ public Country getCountryFromIsoCode(String isoCode) { for (int i = 0; i < countries.size(); i++) { diff --git a/src/main/java/com/heetch/countrypicker/Utils.java b/src/main/java/com/heetch/countrypicker/Utils.java deleted file mode 100644 index 9ee03d3..0000000 --- a/src/main/java/com/heetch/countrypicker/Utils.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.heetch.countrypicker; - -import android.content.Context; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; - -/** - * Created by GODARD Tuatini on 07/05/15. - */ -public class Utils { - - public static int getMipmapResId(Context context, String drawableName) { - return context.getResources().getIdentifier( - drawableName.toLowerCase(Locale.ENGLISH), "mipmap", context.getPackageName()); - } - - public static JSONObject getCountriesJSON(Context context) { - String resourceName = "countries_dialing_code"; - int resourceId = context.getResources().getIdentifier( - resourceName, "raw", context.getApplicationContext().getPackageName()); - - if (resourceId == 0) - return null; - - InputStream stream = context.getResources().openRawResource(resourceId); - - try { - return new JSONObject(convertStreamToString(stream)); - } catch (JSONException e) { - e.printStackTrace(); - } - - return null; - } - - public static List parseCountries(JSONObject jsonCountries) { - List countries = new ArrayList<>(); - Iterator iter = jsonCountries.keys(); - - while (iter.hasNext()) { - String key = iter.next(); - try { - String value = (String) jsonCountries.get(key); - countries.add(new Country(key, value)); - } catch (JSONException e) { - e.printStackTrace(); - } - } - return countries; - } - - public static String convertStreamToString(java.io.InputStream is) { - java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); - return s.hasNext() ? s.next() : ""; - } -} diff --git a/src/main/java/com/heetch/countrypicker/Utils.kt b/src/main/java/com/heetch/countrypicker/Utils.kt new file mode 100644 index 0000000..e83eddd --- /dev/null +++ b/src/main/java/com/heetch/countrypicker/Utils.kt @@ -0,0 +1,59 @@ +package com.heetch.countrypicker + +import android.content.Context +import android.text.Editable +import android.text.TextWatcher +import android.widget.EditText +import org.json.JSONException +import org.json.JSONObject +import java.io.InputStream +import java.util.* + +/** + * Created by GODARD Tuatini on 07/05/15. + */ +object Utils { + @JvmStatic + fun getMipmapResId(context: Context, drawableName: String): Int { + return context.resources.getIdentifier( + drawableName.toLowerCase(Locale.ENGLISH), "mipmap", context.packageName) + } + + @JvmStatic + fun getCountriesJSON(context: Context): JSONObject? { + val resourceName = "countries_dialing_code" + val resourceId = context.resources.getIdentifier( + resourceName, "raw", context.applicationContext.packageName) + if (resourceId == 0) return null + val stream = context.resources.openRawResource(resourceId) + try { + return JSONObject(convertStreamToString(stream)) + } catch (e: JSONException) { + e.printStackTrace() + } + return null + } + + @JvmStatic + fun parseCountries(context: Context, jsonCountries: JSONObject): MutableList { + val countries: MutableList = ArrayList() + val iter = jsonCountries.keys() + while (iter.hasNext()) { + val key = iter.next() + try { + val value = jsonCountries[key] as String + val name = Locale(context.getResources() + .configuration.locale.language, key).displayCountry + countries.add(Country(name, key, value)) + } catch (e: JSONException) { + e.printStackTrace() + } + } + return countries + } + + fun convertStreamToString(`is`: InputStream?): String { + val s = Scanner(`is`).useDelimiter("\\A") + return if (s.hasNext()) s.next() else "" + } +} \ No newline at end of file diff --git a/src/main/res/layout/country_picker.xml b/src/main/res/layout/country_picker.xml index 973dbb5..3c954b2 100644 --- a/src/main/res/layout/country_picker.xml +++ b/src/main/res/layout/country_picker.xml @@ -2,19 +2,26 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical"> - + + - + android:layout_height="match_parent"> + \ No newline at end of file diff --git a/src/main/res/values/color.xml b/src/main/res/values/color.xml new file mode 100644 index 0000000..4faecfa --- /dev/null +++ b/src/main/res/values/color.xml @@ -0,0 +1,6 @@ + + + #6200EE + #3700B3 + #03DAC5 + \ No newline at end of file diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml new file mode 100644 index 0000000..ecea1eb --- /dev/null +++ b/src/main/res/values/styles.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file