├── .gitignore ├── .idea ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── dragosholban │ │ └── com │ │ └── timezoneconverter │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── dragosholban │ │ │ └── com │ │ │ └── timezoneconverter │ │ │ ├── DatePickerFragment.java │ │ │ ├── MainActivity.java │ │ │ ├── SelectTimezonesActivity.java │ │ │ ├── TimeZoneActivity.java │ │ │ └── TimeZoneAdapter.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_select_timezones.xml │ │ └── activity_time_zone.xml │ │ ├── menu │ │ └── options_menu.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── dragosholban │ └── com │ └── timezoneconverter │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidTimeZoneConverter 2 | This is the source code for the tutorial published on my website: [Build Your First Android App: A Time Zone Converter](https://dragosholban.com/2018/02/24/build-your-first-android-app-a-time-zone-converter/) 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | defaultConfig { 6 | applicationId "dragosholban.com.timezoneconverter" 7 | minSdkVersion 16 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(dir: 'libs', include: ['*.jar']) 23 | implementation 'com.android.support:appcompat-v7:26.1.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 28 | implementation 'com.android.support:design:26.1.0' 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/dragosholban/com/timezoneconverter/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package dragosholban.com.timezoneconverter; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("dragosholban.com.timezoneconverter", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/dragosholban/com/timezoneconverter/DatePickerFragment.java: -------------------------------------------------------------------------------- 1 | package dragosholban.com.timezoneconverter; 2 | 3 | import android.app.DatePickerDialog; 4 | import android.app.Dialog; 5 | import android.app.DialogFragment; 6 | import android.os.Bundle; 7 | import android.widget.DatePicker; 8 | 9 | import java.util.Calendar; 10 | import java.util.Date; 11 | 12 | public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener { 13 | 14 | @Override 15 | public Dialog onCreateDialog(Bundle savedInstanceState) { 16 | Calendar c = Calendar.getInstance(); 17 | int year = c.get(Calendar.YEAR); 18 | int month = c.get(Calendar.MONTH); 19 | int day = c.get(Calendar.DAY_OF_MONTH); 20 | 21 | return new DatePickerDialog(getActivity(), this, year, month, day); 22 | } 23 | 24 | @Override 25 | public void onDateSet(DatePicker datePicker, int year, int month, int day) { 26 | ((MainActivity) getActivity()).setLocalDate(new Date(year - 1900, month, day)); 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/dragosholban/com/timezoneconverter/MainActivity.java: -------------------------------------------------------------------------------- 1 | package dragosholban.com.timezoneconverter; 2 | 3 | import android.app.DialogFragment; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.os.Bundle; 9 | import android.view.View; 10 | import android.widget.AdapterView; 11 | import android.widget.ArrayAdapter; 12 | import android.widget.Button; 13 | import android.widget.ListView; 14 | import android.widget.SeekBar; 15 | import android.widget.TextView; 16 | 17 | import java.text.DateFormat; 18 | import java.util.ArrayList; 19 | import java.util.Arrays; 20 | import java.util.Collections; 21 | import java.util.Comparator; 22 | import java.util.Date; 23 | import java.util.HashSet; 24 | import java.util.Set; 25 | import java.util.TimeZone; 26 | 27 | public class MainActivity extends AppCompatActivity { 28 | Date localDate = new Date(); 29 | Button dateBtn; 30 | Button selectTimeZoneBtn; 31 | TimeZone userTimeZone; 32 | String[] selectedTimezones = new String[] {"Europe/Bucharest", "Europe/London", "Europe/Paris"}; 33 | TimeZone selectedTimeZone; 34 | TextView convertedTimeTv; 35 | TextView convertedDateTv; 36 | ListView listView; 37 | ArrayAdapter adapter; 38 | SeekBar seekBar; 39 | 40 | private static int CHOOSE_TIME_ZONE_REQUEST_CODE = 1; 41 | private static int SELECT_TIME_ZONES_REQUEST_CODE = 2; 42 | 43 | @Override 44 | protected void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | setContentView(R.layout.activity_main); 47 | 48 | loadPreferences(); 49 | 50 | seekBar = findViewById(R.id.seekBar); 51 | final TextView userTime = findViewById(R.id.userTime); 52 | convertedTimeTv = findViewById(R.id.convertedTime); 53 | convertedDateTv = findViewById(R.id.convertedDate); 54 | 55 | seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { 56 | @Override 57 | public void onProgressChanged(SeekBar seekBar, int i, boolean fromUser) { 58 | userTime.setText((seekBar.getProgress() < 10 ? "0" + Integer.toString(seekBar.getProgress()) : Integer.toString(seekBar.getProgress())) + ":00"); 59 | localDate.setHours(seekBar.getProgress()); 60 | if (fromUser) { 61 | localDate.setMinutes(0); 62 | } 63 | convertDate(userTimeZone, selectedTimeZone); 64 | } 65 | 66 | @Override 67 | public void onStartTrackingTouch(SeekBar seekBar) { 68 | 69 | } 70 | 71 | @Override 72 | public void onStopTrackingTouch(SeekBar seekBar) { 73 | 74 | } 75 | }); 76 | seekBar.setProgress(localDate.getHours()); 77 | 78 | dateBtn = findViewById(R.id.dateButton); 79 | dateBtn.setText(DateFormat.getDateInstance().format(localDate)); 80 | 81 | selectTimeZoneBtn = findViewById(R.id.timeZoneButton); 82 | 83 | listView = findViewById(R.id.listView); 84 | setupAdapter(); 85 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 86 | @Override 87 | public void onItemClick(AdapterView adapterView, View view, int i, long l) { 88 | selectedTimeZone = TimeZone.getTimeZone(selectedTimezones[i]); 89 | convertDate(userTimeZone, selectedTimeZone); 90 | savePreferences(); 91 | } 92 | }); 93 | 94 | if (userTimeZone != null) { 95 | selectTimeZoneBtn.setText(userTimeZone.getID()); 96 | } 97 | if (selectedTimezones.length > 0 && selectedTimeZone != null) { 98 | int selectedTimeZonePosition = 0; 99 | for (int i = 0; i < this.selectedTimezones.length; i++) { 100 | if (selectedTimeZone.getID().equals(this.selectedTimezones[i])) { 101 | selectedTimeZonePosition = i; 102 | break; 103 | } 104 | } 105 | listView.setItemChecked(selectedTimeZonePosition, true); 106 | listView.setSelection(selectedTimeZonePosition); 107 | selectedTimeZone = TimeZone.getTimeZone(selectedTimezones[selectedTimeZonePosition]); 108 | } 109 | convertDate(userTimeZone, selectedTimeZone); 110 | } 111 | 112 | public void showDatePicker(View view) { 113 | DialogFragment dialog = new DatePickerFragment(); 114 | dialog.show(getFragmentManager(), "datePicker"); 115 | } 116 | 117 | public void setLocalDate(Date date) { 118 | // we need to keep the time on the date unchanged 119 | int hours = localDate.getHours(); 120 | int minutes = localDate.getMinutes(); 121 | localDate = date; 122 | localDate.setHours(hours); 123 | localDate.setMinutes(minutes); 124 | Button dateBtn = findViewById(R.id.dateButton); 125 | dateBtn.setText(DateFormat.getDateInstance().format(localDate)); 126 | convertDate(userTimeZone, selectedTimeZone); 127 | } 128 | 129 | public void chooseTimezone(View view) { 130 | Intent intent = new Intent(this, TimeZoneActivity.class); 131 | startActivityForResult(intent, CHOOSE_TIME_ZONE_REQUEST_CODE); 132 | } 133 | 134 | @Override 135 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 136 | if(requestCode == CHOOSE_TIME_ZONE_REQUEST_CODE && resultCode == RESULT_OK) { 137 | String timezone = data.getStringExtra("timezone"); 138 | selectTimeZoneBtn.setText(timezone); 139 | userTimeZone = TimeZone.getTimeZone(timezone); 140 | } 141 | if (requestCode == SELECT_TIME_ZONES_REQUEST_CODE && resultCode == RESULT_OK) { 142 | Bundle bundle = data.getBundleExtra("selectedTimezonesBundle"); 143 | ArrayList selectedTimezonesArrayList = bundle.getStringArrayList("selectedTimezones"); 144 | Collections.sort(selectedTimezonesArrayList, new Comparator() { 145 | @Override 146 | public int compare(String s, String t1) { 147 | return s.compareToIgnoreCase(t1); 148 | } 149 | }); 150 | selectedTimezones = new String[selectedTimezonesArrayList.size()]; 151 | selectedTimezonesArrayList.toArray(selectedTimezones); 152 | setupAdapter(); 153 | } 154 | convertDate(userTimeZone, selectedTimeZone); 155 | savePreferences(); 156 | } 157 | 158 | private void convertDate(TimeZone fromTimeZone, TimeZone toTimeZone) { 159 | if (fromTimeZone != null && toTimeZone != null) { 160 | long fromOffset = fromTimeZone.getOffset(localDate.getTime()); 161 | long toOffset = toTimeZone.getOffset(localDate.getTime()); 162 | long convertedTime = localDate.getTime() - (fromOffset - toOffset); 163 | Date convertedDate = new Date(convertedTime); 164 | int hours = convertedDate.getHours(); 165 | int minutes = convertedDate.getMinutes(); 166 | String time = (hours < 10 ? "0" + Integer.toString(hours) : Integer.toString(hours)) 167 | + ":" + (minutes < 10 ? "0" + Integer.toString(minutes) : Integer.toString(minutes)); 168 | convertedTimeTv.setText(time); 169 | convertedDateTv.setText(DateFormat.getDateInstance().format(convertedDate)); 170 | } 171 | } 172 | 173 | public void selectTimezones(View view) { 174 | Bundle bundle = new Bundle(); 175 | bundle.putStringArrayList("selectedTimezones", new ArrayList(Arrays.asList(selectedTimezones))); 176 | Intent intent = new Intent(this, SelectTimezonesActivity.class); 177 | intent.putExtra("selectedTimezonesBundle", bundle); 178 | startActivityForResult(intent, 2); 179 | } 180 | 181 | private void setupAdapter() { 182 | adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_activated_1, android.R.id.text1, selectedTimezones); 183 | listView.setAdapter(adapter); 184 | } 185 | 186 | private void savePreferences() { 187 | SharedPreferences preferences = this.getPreferences(Context.MODE_PRIVATE); 188 | SharedPreferences.Editor editor = preferences.edit(); 189 | 190 | if (userTimeZone != null) { 191 | editor.putString("userTimezone", userTimeZone.getID()); 192 | } 193 | 194 | if (selectedTimeZone != null) { 195 | editor.putString("selectedTimezone", selectedTimeZone.getID()); 196 | } 197 | 198 | editor.putStringSet("selectedTimezones", new HashSet<>(Arrays.asList(selectedTimezones))); 199 | 200 | editor.commit(); 201 | } 202 | 203 | private void loadPreferences() { 204 | SharedPreferences preferences = this.getPreferences(Context.MODE_PRIVATE); 205 | 206 | String userTimezone = preferences.getString("userTimezone", TimeZone.getDefault().getID()); 207 | this.userTimeZone = TimeZone.getTimeZone(userTimezone); 208 | 209 | Set defaultSelectedTimezones = new HashSet<>(Arrays.asList(new String[] {"America/Los_Angeles", "America/New_York", "Asia/Hong_Kong", "Asia/Tokyo", "Europe/London", "Europe/Moscow", "Europe/Paris"})); 210 | Set selectedTimezones = preferences.getStringSet("selectedTimezones", defaultSelectedTimezones); 211 | ArrayList selectedTimezonesArrayList = new ArrayList<>(selectedTimezones); 212 | Collections.sort(selectedTimezonesArrayList, new Comparator() { 213 | @Override 214 | public int compare(String s, String t1) { 215 | return s.compareToIgnoreCase(t1); 216 | } 217 | }); 218 | this.selectedTimezones = selectedTimezonesArrayList.toArray(new String[selectedTimezonesArrayList.size()]); 219 | 220 | String selectedTimezoneID = preferences.getString("selectedTimezone", null); 221 | if (selectedTimezoneID != null) { 222 | if (selectedTimezones.contains(selectedTimezoneID)) { 223 | this.selectedTimeZone = TimeZone.getTimeZone(selectedTimezoneID); 224 | } 225 | } 226 | } 227 | 228 | public void convertCurrentDate(View view) { 229 | localDate = new Date(); 230 | convertDate(userTimeZone, selectedTimeZone); 231 | seekBar.setProgress(localDate.getHours()); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /app/src/main/java/dragosholban/com/timezoneconverter/SelectTimezonesActivity.java: -------------------------------------------------------------------------------- 1 | package dragosholban.com.timezoneconverter; 2 | 3 | import android.content.Intent; 4 | import android.support.v4.view.MenuItemCompat; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.support.v7.widget.SearchView; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.widget.AdapterView; 12 | import android.widget.ArrayAdapter; 13 | import android.widget.Button; 14 | import android.widget.Filter; 15 | import android.widget.ListView; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.TimeZone; 20 | 21 | public class SelectTimezonesActivity extends AppCompatActivity { 22 | ArrayList selectedTimezones = new ArrayList<>(); 23 | TimeZoneAdapter adapter; 24 | ArrayList timezones; 25 | ListView listView; 26 | boolean showAll = true; 27 | 28 | @Override 29 | protected void onCreate(Bundle savedInstanceState) { 30 | super.onCreate(savedInstanceState); 31 | setContentView(R.layout.activity_select_timezones); 32 | 33 | setTitle("Choose Time Zones"); 34 | 35 | Intent intent = getIntent(); 36 | Bundle bundle = intent.getBundleExtra("selectedTimezonesBundle"); 37 | selectedTimezones = bundle.getStringArrayList("selectedTimezones"); 38 | timezones = new ArrayList<>(Arrays.asList(TimeZone.getAvailableIDs())); 39 | 40 | listView = findViewById(R.id.listView); 41 | adapter = new TimeZoneAdapter(this, android.R.layout.simple_list_item_multiple_choice, android.R.id.text1, timezones); 42 | listView.setAdapter(adapter); 43 | 44 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 45 | @Override 46 | public void onItemClick(AdapterView adapterView, View view, int i, long l) { 47 | if (listView.isItemChecked(i)) { 48 | selectedTimezones.add(adapter.getItem(i)); 49 | } else { 50 | selectedTimezones.remove(adapter.getItem(i)); 51 | } 52 | } 53 | }); 54 | 55 | checkSelectedTimezones(); 56 | } 57 | 58 | public void done(View view) { 59 | Bundle bundle = new Bundle(); 60 | bundle.putStringArrayList("selectedTimezones", selectedTimezones); 61 | Intent result = new Intent(this, MainActivity.class); 62 | result.putExtra("selectedTimezonesBundle", bundle); 63 | setResult(RESULT_OK, result); 64 | finish(); 65 | } 66 | 67 | public void showChecked(View view) { 68 | Button button = (Button) view; 69 | adapter.clear(); 70 | if (showAll) { 71 | for (String timezone : selectedTimezones) { 72 | adapter.add(timezone); 73 | } 74 | adapter.notifyDataSetChanged(); 75 | 76 | button.setText("Show All"); 77 | showAll = false; 78 | } else { 79 | for (String timezone : TimeZone.getAvailableIDs()) { 80 | adapter.add(timezone); 81 | } 82 | adapter.notifyDataSetChanged(); 83 | 84 | button.setText("Show Checked"); 85 | showAll = true; 86 | } 87 | 88 | checkSelectedTimezones(); 89 | } 90 | 91 | public void uncheckAll(View view) { 92 | selectedTimezones.clear(); 93 | checkSelectedTimezones(); 94 | } 95 | 96 | private void checkSelectedTimezones() { 97 | for(int j = 0; j < adapter.getCount(); j++) { 98 | if (selectedTimezones.contains(adapter.getItem(j))) { 99 | listView.setItemChecked(j, true); 100 | } else { 101 | listView.setItemChecked(j, false); 102 | } 103 | } 104 | } 105 | 106 | @Override 107 | public boolean onCreateOptionsMenu(Menu menu) { 108 | getMenuInflater().inflate(R.menu.options_menu, menu); 109 | 110 | MenuItem searchItem = menu.findItem(R.id.action_search); 111 | SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem); 112 | 113 | searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { 114 | @Override 115 | public boolean onQueryTextSubmit(String s) { 116 | return false; 117 | } 118 | 119 | @Override 120 | public boolean onQueryTextChange(String s) { 121 | adapter.getFilter().filter(s, new Filter.FilterListener() { 122 | @Override 123 | public void onFilterComplete(int i) { 124 | checkSelectedTimezones(); 125 | } 126 | }); 127 | 128 | return true; 129 | } 130 | }); 131 | 132 | return true; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/java/dragosholban/com/timezoneconverter/TimeZoneActivity.java: -------------------------------------------------------------------------------- 1 | package dragosholban.com.timezoneconverter; 2 | 3 | import android.content.Intent; 4 | import android.support.v4.view.MenuItemCompat; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.support.v7.widget.SearchView; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.widget.AdapterView; 12 | import android.widget.ArrayAdapter; 13 | import android.widget.ListView; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Arrays; 17 | import java.util.TimeZone; 18 | 19 | public class TimeZoneActivity extends AppCompatActivity { 20 | TimeZoneAdapter adapter; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_time_zone); 26 | 27 | ListView listView = findViewById(R.id.listView); 28 | final ArrayList timezones = new ArrayList<>(Arrays.asList(TimeZone.getAvailableIDs())); 29 | adapter = new TimeZoneAdapter(this, android.R.layout.simple_list_item_1, android.R.id.text1, timezones); 30 | listView.setAdapter(adapter); 31 | 32 | listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { 33 | @Override 34 | public void onItemClick(AdapterView adapterView, View view, int i, long l) { 35 | Intent result = new Intent(getApplicationContext(), MainActivity.class); 36 | result.putExtra("timezone", timezones.get(i)); 37 | setResult(RESULT_OK, result); 38 | finish(); 39 | } 40 | }); 41 | } 42 | 43 | @Override 44 | public boolean onCreateOptionsMenu(Menu menu) { 45 | getMenuInflater().inflate(R.menu.options_menu, menu); 46 | 47 | MenuItem searchItem = menu.findItem(R.id.action_search); 48 | SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem); 49 | 50 | searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { 51 | @Override 52 | public boolean onQueryTextSubmit(String s) { 53 | return false; 54 | } 55 | 56 | @Override 57 | public boolean onQueryTextChange(String s) { 58 | adapter.getFilter().filter(s); 59 | 60 | return true; 61 | } 62 | }); 63 | 64 | return true; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/dragosholban/com/timezoneconverter/TimeZoneAdapter.java: -------------------------------------------------------------------------------- 1 | package dragosholban.com.timezoneconverter; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.widget.ArrayAdapter; 6 | import android.widget.Filter; 7 | 8 | import java.util.ArrayList; 9 | 10 | public class TimeZoneAdapter extends ArrayAdapter { 11 | 12 | private ArrayList original; 13 | private Filter filter; 14 | 15 | public TimeZoneAdapter(@NonNull Context context, int resource, int textViewResourceId, @NonNull ArrayList objects) { 16 | super(context, resource, textViewResourceId, objects); 17 | original = new ArrayList<>(objects); 18 | } 19 | 20 | @NonNull 21 | @Override 22 | public Filter getFilter() { 23 | return new TimeZoneFilter(); 24 | } 25 | 26 | private class TimeZoneFilter extends Filter { 27 | 28 | @Override 29 | protected FilterResults performFiltering(CharSequence charSequence) { 30 | FilterResults results = new FilterResults(); 31 | ArrayList filtered = new ArrayList<>(); 32 | String search = charSequence.toString().toLowerCase(); 33 | 34 | if (search == null || search.length() == 0) { 35 | filtered = new ArrayList<>(original); 36 | } else { 37 | for (int i = 0; i < original.size(); i++) { 38 | if (original.get(i).toLowerCase().contains(charSequence)) { 39 | filtered.add(original.get(i)); 40 | } 41 | } 42 | } 43 | 44 | results.values = filtered; 45 | results.count = filtered.size(); 46 | 47 | return results; 48 | } 49 | 50 | @Override 51 | protected void publishResults(CharSequence charSequence, FilterResults filterResults) { 52 | ArrayList items = (ArrayList) filterResults.values; 53 | clear(); 54 | for (int i = 0; i < items.size(); i++) { 55 | add(items.get(i)); 56 | } 57 | 58 | notifyDataSetChanged(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |