diff --git a/app/build.gradle b/app/build.gradle index 9ba0f37..94e950c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -44,7 +44,7 @@ dependencies { androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - + def supportLibVersion = '24.2.0' compile "com.android.support:appcompat-v7:${supportLibVersion}" compile "com.android.support:cardview-v7:${supportLibVersion}" diff --git a/app/src/main/java/com/g11x/checklistapp/AppPreferences.java b/app/src/main/java/com/g11x/checklistapp/AppPreferences.java new file mode 100644 index 0000000..0d71a14 --- /dev/null +++ b/app/src/main/java/com/g11x/checklistapp/AppPreferences.java @@ -0,0 +1,108 @@ +/* + * Copyright © 2016 Google Inc. + * + * 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.g11x.checklistapp; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; + +import com.g11x.checklistapp.language.Language; +import com.g11x.checklistapp.language.PreferredLanguageSupport; + +/** + * Simplified class to access the app's {@link android.content.SharedPreferences}. + */ + +public class AppPreferences { + + private static final String PREF_PREFERRED_LANGUAGE = "PREF_PREFERRED_LANGUAGE"; + + /** + * Returns whether the user has chosen to override the system's default language. + * + * @param context component context used to retrieve shared preferences + */ + public static boolean isLanguageOverriden(@NonNull Context context) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + String value = sp.getString(PREF_PREFERRED_LANGUAGE, null); + return TextUtils.isEmpty(value); + } + + /** + * Saves the user's language preference which overrides the system default. + * + * @param context component context used to retrieve shared preferences + * @param language the language identifier or null to unset + */ + public static void setLanguageOverride(@NonNull Context context, + @Nullable Language language) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = sp.edit(); + if (language == null) { + editor.remove(PREF_PREFERRED_LANGUAGE); + } else { + editor.putString(PREF_PREFERRED_LANGUAGE, language.name()); + } + editor.apply(); + } + + /** + * Returns the language override or null when not set. + * + * @param context component context used to retrieve shared preferences + */ + @Nullable + public static Language getLanguageOverride(@NonNull Context context) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + String value = sp.getString(PREF_PREFERRED_LANGUAGE, null); + if (value == null) { + return null; + } + return Language.valueOf(value); + } + + /** + * Custom shared preference listener for the preferred language property. + */ + public static abstract class LanguageChangeListener { + private final SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) { + if (s.equals(PREF_PREFERRED_LANGUAGE)) { + onChanged(sharedPreferences.getString(s, null)); + } + } + }; + + LanguageChangeListener(@NonNull Context context) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + sp.registerOnSharedPreferenceChangeListener(listener); + } + + public void unregister(@NonNull Context context) { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); + sp.unregisterOnSharedPreferenceChangeListener(listener); + } + + public abstract void onChanged(String newValue); + } + +} diff --git a/app/src/main/java/com/g11x/checklistapp/ChecklistActivity.java b/app/src/main/java/com/g11x/checklistapp/ChecklistActivity.java index 58c656a..7c0b8f3 100644 --- a/app/src/main/java/com/g11x/checklistapp/ChecklistActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/ChecklistActivity.java @@ -29,12 +29,15 @@ import com.firebase.ui.database.FirebaseRecyclerAdapter; import com.g11x.checklistapp.data.ChecklistItem; import com.g11x.checklistapp.data.Database; +import com.g11x.checklistapp.language.Language; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; public class ChecklistActivity extends NavigationActivity { private FirebaseRecyclerAdapter checklistAdapter; + private AppPreferences.LanguageChangeListener languageChangeListener; + private Language language; @Override protected int getNavDrawerItemIndex() { @@ -46,6 +49,15 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_checklist); + language = AppPreferences.getLanguageOverride(this); + languageChangeListener = new AppPreferences.LanguageChangeListener(this) { + + @Override + public void onChanged(String newValue) { + ChecklistActivity.this.onLanguageChange(newValue); + } + }; + DatabaseReference databaseRef = FirebaseDatabase.getInstance().getReference() .child("checklists") .child("basic"); @@ -66,8 +78,8 @@ public void onBindViewHolder(ChecklistItemHolder viewHolder, int position) { @Override protected void populateViewHolder( final ChecklistItemHolder itemHolder, ChecklistItem model, final int position) { - itemHolder.setText(model.getName()); itemHolder.markDone(model.isDone(getContentResolver())); + itemHolder.setText(model.getName(language)); itemHolder.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -96,14 +108,19 @@ protected void onStart() { } } + private void onLanguageChange(String newValue) { + language = Language.valueOf(newValue); + } + @Override protected void onDestroy() { super.onDestroy(); checklistAdapter.cleanup(); + languageChangeListener.unregister(this); } public static class ChecklistItemHolder extends RecyclerView.ViewHolder { - View view; + final View view; public ChecklistItemHolder(View itemView) { super(itemView); diff --git a/app/src/main/java/com/g11x/checklistapp/ChecklistItemActivity.java b/app/src/main/java/com/g11x/checklistapp/ChecklistItemActivity.java index 9016b4a..11d2971 100644 --- a/app/src/main/java/com/g11x/checklistapp/ChecklistItemActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/ChecklistItemActivity.java @@ -28,6 +28,7 @@ import android.widget.ToggleButton; import com.g11x.checklistapp.data.ChecklistItem; +import com.g11x.checklistapp.language.Language; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; import com.google.firebase.database.DatabaseReference; @@ -39,13 +40,25 @@ public class ChecklistItemActivity extends AppCompatActivity { private ChecklistItem checklistItem; private ToggleButton isDone; private DatabaseReference databaseRef; + private Language language; + private AppPreferences.LanguageChangeListener languageChangeListener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); setContentView(R.layout.activity_checklist_item); - databaseRef = FirebaseDatabase.getInstance().getReferenceFromUrl( + language = AppPreferences.getLanguageOverride(this); + languageChangeListener = new AppPreferences.LanguageChangeListener(this) { + + @Override + public void onChanged(String newValue) { + ChecklistItemActivity.this.onLanguageChange(newValue); + } + }; + + DatabaseReference databaseRef = FirebaseDatabase.getInstance().getReferenceFromUrl( this.getIntent().getStringExtra("databaseRefUrl")); databaseRef.addValueEventListener(new ValueEventListener() { @@ -63,12 +76,22 @@ public void onCancelled(DatabaseError databaseError) { } + private void onLanguageChange(String newValue) { + language = Language.valueOf(newValue); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + languageChangeListener.unregister(this); + } + private void createUI() { TextView name = (TextView) findViewById(R.id.name); - name.setText(checklistItem.getName()); + name.setText(checklistItem.getName(language)); TextView description = (TextView) findViewById(R.id.description); - description.setText(checklistItem.getDescription()); + description.setText(checklistItem.getDescription(language)); Button getDirections = (Button) findViewById(R.id.directions); getDirections.setOnClickListener(new View.OnClickListener() { @@ -88,11 +111,6 @@ public void onClick(View view) { }); } - @Override - protected void onStart() { - super.onStart(); - } - private void onClickIsDone() { ContentResolver contentResolver = getContentResolver(); checklistItem.setDone(contentResolver, !checklistItem.isDone(contentResolver)); diff --git a/app/src/main/java/com/g11x/checklistapp/ImportantInformationActivity.java b/app/src/main/java/com/g11x/checklistapp/ImportantInformationActivity.java index 26b8fc9..9c39cc1 100644 --- a/app/src/main/java/com/g11x/checklistapp/ImportantInformationActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/ImportantInformationActivity.java @@ -25,16 +25,19 @@ import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.os.Bundle; +import android.support.v7.widget.CardView; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; import android.widget.TextView; import com.g11x.checklistapp.data.Database; +import org.w3c.dom.Text; + import java.util.ArrayList; /** @@ -74,6 +77,8 @@ public void onClick(View view) { } }); + final TextView emptyListInfo = (TextView) findViewById(R.id.important_information_empty); + Intent intent = getIntent(); if (intent.getExtras() != null && intent.getExtras().get("title") != null) { @@ -93,6 +98,8 @@ public Loader onCreateLoader(int i, Bundle bundle) { @Override public void onLoadFinished(Loader loader, Cursor cursor) { + refreshUi(cursor.getCount()); + if (adapter == null) { if (cursor != null) { adapter = new Adapter(cursor); @@ -107,6 +114,17 @@ public void onLoadFinished(Loader loader, Cursor cursor) { public void onLoaderReset(Loader loader) { adapter.swapCursor(null); } + + private void refreshUi(int itemCount) { + if (itemCount > 0) { + recyclerView.setVisibility(View.VISIBLE); + emptyListInfo.setVisibility(View.GONE); + } else { + emptyListInfo.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.GONE); + } + + } }); } @@ -131,8 +149,8 @@ private static class Adapter extends RecyclerViewCursorAdapter { - private String[] dataSet; - - LanguageSelectionAdapter() { - dataSet = new String[] { "Device default (English)", "English", "Espanol", "Russian" }; - } + private Language[] dataSet = Language.values(); @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { @@ -62,7 +62,8 @@ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { @Override public void onBindViewHolder(ViewHolder holder, int position) { - holder.textView.setText(dataSet[position]); + holder.language = dataSet[position]; + holder.textView.setText(dataSet[position].getNativeDescription()); } @Override @@ -72,6 +73,7 @@ public int getItemCount() { class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private TextView textView; + private Language language; ViewHolder(final CardView view) { super(view); textView = (TextView)view.findViewById(R.id.checkbox); @@ -80,7 +82,8 @@ class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener @Override public void onClick(View view) { - + Log.w(LOG_TAG, "Setting language to: " + language); + AppPreferences.setLanguageOverride(view.getContext(), language); } } } diff --git a/app/src/main/java/com/g11x/checklistapp/LocalRepository.java b/app/src/main/java/com/g11x/checklistapp/LocalRepository.java index d8f1a29..c1590e1 100644 --- a/app/src/main/java/com/g11x/checklistapp/LocalRepository.java +++ b/app/src/main/java/com/g11x/checklistapp/LocalRepository.java @@ -51,7 +51,7 @@ public Uri insert(@NonNull Uri uri, ContentValues contentValues) { @Override public int delete(@NonNull Uri uri, String s, String[] strings) { - return 0; + throw new UnsupportedOperationException(); } @Override diff --git a/app/src/main/java/com/g11x/checklistapp/NavigationActivity.java b/app/src/main/java/com/g11x/checklistapp/NavigationActivity.java index 4b531d2..3843412 100644 --- a/app/src/main/java/com/g11x/checklistapp/NavigationActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/NavigationActivity.java @@ -17,7 +17,7 @@ package com.g11x.checklistapp; -import android.app.ActivityOptions; +import android.app.Activity; import android.content.Intent; import android.content.res.Configuration; import android.os.Build; @@ -26,6 +26,7 @@ import android.support.v4.app.TaskStackBuilder; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.view.MenuItem; @@ -34,7 +35,7 @@ import android.widget.ArrayAdapter; import android.widget.ListView; -import com.g11x.checklistapp.data.Notification; +import com.g11x.checklistapp.language.PreferredLanguageSupport; import java.util.ArrayList; @@ -44,10 +45,11 @@ public abstract class NavigationActivity extends AppCompatActivity { private DrawerLayout drawerLayout; private ListView drawerList; private ActionBarDrawerToggle drawerToggle; + private AppPreferences.LanguageChangeListener baseLanguageChangeListener; private CharSequence title; - protected static final int NAVDRAWER_ITEM_CHECKLIST = 0; + static final int NAVDRAWER_ITEM_CHECKLIST = 0; protected static final int NAVDRAWER_ITEM_IMPORTANT_INFO = 1; protected static final int NAVDRAWER_ITEM_NOTIFICATIONS = 2; protected static final int NAVDRAWER_ITEM_LANGUAGE = 3; @@ -66,6 +68,15 @@ public abstract class NavigationActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); + PreferredLanguageSupport.applyPreferredLanguage(this); + final Activity thisActivity = NavigationActivity.this; + baseLanguageChangeListener = new AppPreferences.LanguageChangeListener(NavigationActivity.this) { + @Override + public void onChanged(String newValue) { + thisActivity.recreate(); + } + }; } @Override @@ -74,21 +85,21 @@ public void setContentView(@LayoutRes int layoutResID) { setUpDrawer(); } - final protected void setUpDrawer() { + private void setUpDrawer() { title = getTitle(); drawerLayout = (DrawerLayout) findViewById(R.id.main_layout); drawerList = (ListView) findViewById(R.id.left_drawer); ArrayList navDrawerTitles = new ArrayList<>(); - for (int i = 0; i < NAVDRAWER_TITLE_RES_IDS.length; i++) { - navDrawerTitles.add(getResources().getString(NAVDRAWER_TITLE_RES_IDS[i])); + for (int NAVDRAWER_TITLE_RES_ID : NAVDRAWER_TITLE_RES_IDS) { + navDrawerTitles.add(getResources().getString(NAVDRAWER_TITLE_RES_ID)); } // set a custom shadow that overlays the main content when the drawer opens drawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); // set up the drawer's list view with items and click listener - drawerList.setAdapter(new ArrayAdapter( + drawerList.setAdapter(new ArrayAdapter<>( this, R.layout.drawer_list_item, navDrawerTitles)); drawerList.setOnItemClickListener(new DrawerItemClickListener()); @@ -122,10 +133,11 @@ public void onDrawerOpened(View drawerView) { @Override protected void onDestroy() { drawerLayout.removeDrawerListener(drawerToggle); + baseLanguageChangeListener.unregister(this); super.onDestroy(); } - /* The click listner for ListView in the navigation drawer */ + /* The click listener for ListView in the navigation drawer */ private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { @@ -133,7 +145,7 @@ public void onItemClick(AdapterView parent, View view, int position, long id) } } - protected void selectItem(int index) { + private void selectItem(int index) { if (getNavDrawerItemIndex() != index) { @@ -176,7 +188,10 @@ private void createBackStack(Intent intent) { @Override public void setTitle(CharSequence title) { this.title = title; - getSupportActionBar().setTitle(this.title); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setTitle(this.title); + } } /** @@ -193,7 +208,7 @@ protected void onPostCreate(Bundle savedInstanceState) { @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - // Pass any configuration change to the drawer toggls + // Pass any configuration change to the drawer toggles drawerToggle.onConfigurationChanged(newConfig); } @@ -201,10 +216,7 @@ public void onConfigurationChanged(Configuration newConfig) { public boolean onOptionsItemSelected(MenuItem item) { // The action bar home/up action should open or close the drawer. // ActionBarDrawerToggle will take care of this. - if (drawerToggle.onOptionsItemSelected(item)) { - return true; - } - return super.onOptionsItemSelected(item); + return drawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item); } @Override diff --git a/app/src/main/java/com/g11x/checklistapp/NotificationListActivity.java b/app/src/main/java/com/g11x/checklistapp/NotificationListActivity.java index e98f18b..7de6b5a 100644 --- a/app/src/main/java/com/g11x/checklistapp/NotificationListActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/NotificationListActivity.java @@ -1,6 +1,11 @@ package com.g11x.checklistapp; +import android.content.ContentValues; +import android.database.Cursor; import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; @@ -9,10 +14,14 @@ import android.view.ViewGroup; import android.widget.TextView; +import com.g11x.checklistapp.data.Database; import com.g11x.checklistapp.data.Notification; public class NotificationListActivity extends NavigationActivity { + private NotificationAdapter adapter; + private RecyclerView mRecyclerView; + @Override protected int getNavDrawerItemIndex() { return NAVDRAWER_ITEM_NOTIFICATIONS; @@ -22,7 +31,7 @@ protected int getNavDrawerItemIndex() { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_notification_list); - RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view); + mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view); // use this setting to improve performance if you know that changes // in content do not change the layout size of the RecyclerView @@ -40,58 +49,74 @@ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHol @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { - // TODO: Remove item from DB (when db exists). + long id = ((NotificationAdapter.ViewHolder) viewHolder).id; + + ContentValues newValues = new ContentValues(); + newValues.put(Database.Notification.READ_COLUMN, true); + + getContentResolver().update( + Database.Notification.CONTENT_URI, + newValues, + Database.ID_COLUMN + " = " + id, + null + ); } }); helper.attachToRecyclerView(mRecyclerView); - // TODO: Replace dataset with db cursor. - Notification[] dataset = new Notification[]{ - new Notification("message 1", null), - new Notification("message 2", "I'm a title!"), - new Notification("message C", "I can't count :("), - new Notification("I like fish, fishy fishy fish.", null) - }; - RecyclerView.Adapter mAdapter = new NotificationAdapter(dataset); - mRecyclerView.setAdapter(mAdapter); - } - - public class NotificationAdapter extends RecyclerView.Adapter { - private final Notification[] mDataset; + getSupportLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks() { + @Override + public Loader onCreateLoader(int i, Bundle bundle) { + Loader cursor = new CursorLoader(NotificationListActivity.this, + Database.Notification.CONTENT_URI, + Database.Notification.PROJECTION, "NOT " + Database.Notification.READ_COLUMN, null, Database.Notification.SENT_TIME); + return cursor; + } - // Provide a reference to the views for each data item - // Complex data items may need more than one view per item, and - // you provide access to all the views for a data item in a view holder - public class ViewHolder extends RecyclerView.ViewHolder { - // each data item is just a string in this case - public final TextView mTitle; - public final TextView mBody; + @Override + public void onLoadFinished(Loader loader, Cursor cursor) { + if (adapter == null) { + if (cursor != null) { + adapter = new NotificationAdapter(cursor); + mRecyclerView.setAdapter(adapter); + } + } else { + adapter.swapCursor(cursor); + } + } - public ViewHolder(View v) { - super(v); - mTitle = (TextView) v.findViewById(R.id.notification_title); - mBody = (TextView) v.findViewById(R.id.notification_body); + @Override + public void onLoaderReset(Loader loader) { + adapter.swapCursor(null); } + }); + } + + @Override + protected void onStart() { + super.onStart(); + if (adapter != null) { + adapter.notifyDataSetChanged(); } + } + + private static class NotificationAdapter extends RecyclerViewCursorAdapter { - // Provide a suitable constructor (depends on the kind of dataset) - public NotificationAdapter(Notification[] myDataset) { - mDataset = myDataset; + NotificationAdapter(Cursor cursor) { + super(cursor); } - // Create new views (invoked by the layout manager) @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { - // create a new view - View v = LayoutInflater.from(parent.getContext()) + View layout = LayoutInflater.from(parent.getContext()) .inflate(R.layout.view_notification_item, parent, false); - return new ViewHolder(v); + return new ViewHolder(layout); } - // Replace the contents of a view (invoked by the layout manager) @Override - public void onBindViewHolder(ViewHolder holder, int position) { - Notification n = mDataset[position]; + public void onBindViewHolder(ViewHolder holder, Cursor cursor) { + Notification n = Notification.fromCursor(cursor); + holder.id = n.getId(); holder.mBody.setText(n.getMessage()); String title = n.getTitle(); if (title != null && !title.isEmpty()) { @@ -102,10 +127,16 @@ public void onBindViewHolder(ViewHolder holder, int position) { } } - // Return the size of your dataset (invoked by the layout manager) - @Override - public int getItemCount() { - return mDataset.length; + static class ViewHolder extends RecyclerView.ViewHolder { + public final TextView mTitle; + public final TextView mBody; + public long id; + + public ViewHolder(View v) { + super(v); + mTitle = (TextView) v.findViewById(R.id.notification_title); + mBody = (TextView) v.findViewById(R.id.notification_body); + } } } } diff --git a/app/src/main/java/com/g11x/checklistapp/data/ChecklistItem.java b/app/src/main/java/com/g11x/checklistapp/data/ChecklistItem.java index a5666b0..1450a9e 100644 --- a/app/src/main/java/com/g11x/checklistapp/data/ChecklistItem.java +++ b/app/src/main/java/com/g11x/checklistapp/data/ChecklistItem.java @@ -21,9 +21,14 @@ import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; +import android.support.annotation.Nullable; +import com.g11x.checklistapp.language.Language; import com.google.firebase.database.IgnoreExtraProperties; +import java.util.Map; + +@SuppressWarnings("unused") @IgnoreExtraProperties public class ChecklistItem { private String name; @@ -42,6 +47,8 @@ public class ChecklistItem { }; public static final int ITEM_HASH_COLUMN_INDEX = 1; public static final int DONE_COLUMN_INDEX = 2; + private Map> alt; + private boolean isDone; public ChecklistItem() { // Default constructor required for calls to DataSnapshot.getValue(ChecklistItem.class) @@ -58,6 +65,7 @@ private ChecklistItem( this.phone = phone; } + @SuppressWarnings("WeakerAccess") public String getName() { return name; } @@ -67,10 +75,38 @@ private Cursor getCursorForItem(ContentResolver contentResolver) { Database.ChecklistItem.CONTENT_URI, ChecklistItem.PROJECTION, Database.ChecklistItem.ITEM_HASH_COLUMN + " = ?", - new String[] { getHash() }, + new String[]{getHash()}, null); } + public String getName(@Nullable Language language) { + if (language == null) { + return getName(); + } + + if (alt == null) { + return name; + } + + Map languageStrings = alt.get(language.getCode()); + + if (languageStrings == null) { + return name; + } + + String string = languageStrings.get("name"); + + if (string == null) { + return name; + } else { + return string; + } + } + + public boolean isDone() { + return isDone; + } + public boolean isDone(ContentResolver contentResolver) { Cursor cursor = getCursorForItem(contentResolver); int done = 0; @@ -97,10 +133,6 @@ public void setDone(ContentResolver contentResolver, boolean done) { } } - public static ChecklistItem of(String name, String description, String location, Uri directions, String email, String phone) { - return new ChecklistItem(name, description, location, directions.toString(), email, phone); - } - @SuppressWarnings("unused") public String getEmail() { return email; @@ -116,10 +148,35 @@ public String getLocation() { return location; } + @SuppressWarnings("WeakerAccess") public String getDescription() { return description; } + public String getDescription(@Nullable Language language) { + if (language == null) { + return getDescription(); + } + + if (alt == null) { + return description; + } + + Map languageStrings = alt.get(language.getCode()); + + if (languageStrings == null) { + return description; + } + + String string = languageStrings.get("description"); + + if (string == null) { + return description; + } else { + return string; + } + } + @SuppressWarnings("unused") public String getDirectionsUrl() { return directionsUrl; @@ -138,4 +195,9 @@ public String getHash() { } return hash; } + + @SuppressWarnings("unused") + public Map> getAlt() { + return alt; + } } diff --git a/app/src/main/java/com/g11x/checklistapp/data/Database.java b/app/src/main/java/com/g11x/checklistapp/data/Database.java index 71fe270..eba6c4a 100644 --- a/app/src/main/java/com/g11x/checklistapp/data/Database.java +++ b/app/src/main/java/com/g11x/checklistapp/data/Database.java @@ -178,7 +178,16 @@ public static class Notification { public static final String TITLE_COLUMN = "title"; public static final String MESSAGE_COLUMN = "message"; - public static final String READ_COLUMN = "item_hash"; + public static final String READ_COLUMN = "read"; + public static final String SENT_TIME = "sent_time"; + + public static final String[] PROJECTION = { + Database.ID_COLUMN, + SENT_TIME, + READ_COLUMN, + TITLE_COLUMN, + MESSAGE_COLUMN + }; /** * SQL statement for creating this table. @@ -198,13 +207,13 @@ private static class NotificationTableHandler implements TableHandler { @Override public Uri insert(ContentValues contentValues) { - long id = db.insert(Database.ChecklistItem.TABLE_NAME, null, contentValues); + long id = db.insert(TABLE_NAME, null, contentValues); return ContentUris.withAppendedId(contentUri, id); } @Override public Cursor query(String[] projection, String selection, String[] selectionArgs, String sortOrder) { - return null; + return db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, null); } @Override @@ -215,11 +224,14 @@ public int update(ContentValues contentValues, String selection, String[] select private static Uri createContentUri() { // TODO: Figure out how to use getString(R.string.content_provider_authority) here. - return Uri.parse("content://" + COM_G11X_CHECKLISTAPP_PROVIDER + "/" + Database.NAME + "/" + TABLE_NAME); + return Uri.parse("content://" + COM_G11X_CHECKLISTAPP_PROVIDER + "/" + Database.NAME + "/" + + TABLE_NAME); } private static String createCreateTableSql() { - return "create table " + TABLE_NAME + " (_ID integer primary key, " + TITLE_COLUMN + " text, " + MESSAGE_COLUMN + " text, " + READ_COLUMN + " boolean);"; + return "create table " + TABLE_NAME + " (" + ID_COLUMN + " integer primary key, " + + TITLE_COLUMN + " text, " + MESSAGE_COLUMN + " text, " + READ_COLUMN + " boolean, " + + SENT_TIME + " integer);"; } } diff --git a/app/src/main/java/com/g11x/checklistapp/data/Notification.java b/app/src/main/java/com/g11x/checklistapp/data/Notification.java index 1820c76..3330abb 100644 --- a/app/src/main/java/com/g11x/checklistapp/data/Notification.java +++ b/app/src/main/java/com/g11x/checklistapp/data/Notification.java @@ -17,14 +17,21 @@ package com.g11x.checklistapp.data; -import android.support.annotation.Nullable; +import android.database.Cursor; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; /** * Stored representation of a Firebase notification. */ - public class Notification { + @NonNull + private final Long id; + + @NonNull + private final Long sentTime; + + private final boolean read; @Nullable private final String title; @@ -32,11 +39,48 @@ public class Notification { @NonNull private final String message; - public Notification(@NonNull String message, @Nullable String title) { + public Notification( + @NonNull Long id, + @NonNull Long sentTime, + boolean read, + @Nullable String title, + @NonNull String message) { + this.id = id; + this.sentTime = sentTime; + this.read = read; this.message = message; this.title = title; } + /** + * Convenience method to convert a {@code Cursor} to {@code Notification}. Field order found in + * {@code com.g11x.checklistapp.data.Database.Notification.PROJECTION}. + */ + public static + @NonNull + Notification fromCursor(Cursor cursor) { + return new Notification( + cursor.getLong(0), + cursor.getLong(1), + cursor.getShort(2) > 0, + cursor.getString(3), + cursor.getString(4)); + } + + @NonNull + public Long getId() { + return id; + } + + @NonNull + public Long getSentTime() { + return sentTime; + } + + public boolean isRead() { + return read; + } + @Nullable public String getTitle() { return title; diff --git a/app/src/main/java/com/g11x/checklistapp/language/Language.java b/app/src/main/java/com/g11x/checklistapp/language/Language.java new file mode 100644 index 0000000..1587dca --- /dev/null +++ b/app/src/main/java/com/g11x/checklistapp/language/Language.java @@ -0,0 +1,50 @@ +/* + * Copyright © 2016 Google Inc. + * + * 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.g11x.checklistapp.language; + +import android.support.annotation.NonNull; + +/** + * Custom content languages supported by the app. + */ +public enum Language { + Russian("ru", "Русский"), + English("en", "English"), + Ukrainian("uk", "Українська"); + + @NonNull + private String code; + + @NonNull + private String nativeDescription; + + Language(@NonNull String langCode, @NonNull String nativeDesc) { + code = langCode; + nativeDescription = nativeDesc; + } + + @NonNull + public String getCode() { + return code; + } + + @NonNull + public String getNativeDescription() { + return nativeDescription; + } +} diff --git a/app/src/main/java/com/g11x/checklistapp/language/PreferredLanguageSupport.java b/app/src/main/java/com/g11x/checklistapp/language/PreferredLanguageSupport.java new file mode 100644 index 0000000..5ac841a --- /dev/null +++ b/app/src/main/java/com/g11x/checklistapp/language/PreferredLanguageSupport.java @@ -0,0 +1,70 @@ +/* + * Copyright © 2016 Google Inc. + * + * 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.g11x.checklistapp.language; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.os.Build; +import android.support.annotation.NonNull; +import android.util.DisplayMetrics; + +import com.g11x.checklistapp.AppPreferences; + +import java.util.Locale; + +/** + * Delegate methods that can be used to apply the user overriden preferred language to a + * {@link Context}. + */ +public class PreferredLanguageSupport { + public static void applyPreferredLanguage(@NonNull Context context) { + Language language = AppPreferences.getLanguageOverride(context); + if (language != null) { + applyPreferredDefaultLanguage(context, language); + } else { + unsetPreferredDefaultLanguage(context); + } + } + + private static void applyPreferredDefaultLanguage(@NonNull Context context, @NonNull Language language) { + Resources resources = context.getResources(); + DisplayMetrics dm = resources.getDisplayMetrics(); + Configuration conf = resources.getConfiguration(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + //noinspection deprecation Can't remove until minSdk > 17 + conf.locale = new Locale(language.getCode()); + } else { + conf.setLocale(new Locale(language.getCode())); + } + resources.updateConfiguration(conf, dm); + } + + private static void unsetPreferredDefaultLanguage(@NonNull Context context) { + Resources resources = context.getResources(); + DisplayMetrics dm = resources.getDisplayMetrics(); + Configuration conf = resources.getConfiguration(); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + //noinspection deprecation Can't remove until minSdk > 17 + conf.locale = Locale.getDefault(); + } else { + conf.setLocale(Locale.getDefault()); + } + resources.updateConfiguration(conf, dm); + } +} diff --git a/app/src/main/java/com/g11x/checklistapp/services/NotificationService.java b/app/src/main/java/com/g11x/checklistapp/services/NotificationService.java index ff13ae3..1fca254 100644 --- a/app/src/main/java/com/g11x/checklistapp/services/NotificationService.java +++ b/app/src/main/java/com/g11x/checklistapp/services/NotificationService.java @@ -18,8 +18,10 @@ package com.g11x.checklistapp.services; +import android.content.ContentValues; import android.util.Log; +import com.g11x.checklistapp.data.Database; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; @@ -30,16 +32,29 @@ public class NotificationService extends FirebaseMessagingService { private static final String TAG = "NotificationService"; - public void onMessageReceived (RemoteMessage message) { + public void onMessageReceived(RemoteMessage message) { super.onMessageReceived(message); + RemoteMessage.Notification notification = message.getNotification(); + Log.d(TAG, "Received notification: " + message.getNotification().getBody()); - // TODO: Route message to DB, specifically message.getNotification().getBody() && getTitle(). // NOTE: Message expiration date not present in message payload, appears to only be server-side. - - // TODO: Update notification view to be bound to DB and auto-reflect updates in reverse - // chronological order. + ContentValues newValues = new ContentValues(); + newValues.put(Database.Notification.MESSAGE_COLUMN, notification.getBody()); + newValues.put(Database.Notification.READ_COLUMN, false); + newValues.put(Database.Notification.SENT_TIME, message.getSentTime()); + String title = notification.getTitle(); + if (title != null && !title.isEmpty()) { + newValues.put(Database.Notification.TITLE_COLUMN, title); + } + getContentResolver().insert( + Database.Notification.CONTENT_URI, + newValues + ); + + // TODO: Provide some affordance to display the message when the app is in the foreground. + // See https://firebase.google.com/docs/cloud-messaging/android/receive for more details. } } diff --git a/app/src/main/res/layout/activity_important_information.xml b/app/src/main/res/layout/activity_important_information.xml index 44f6ea5..0c6aece 100644 --- a/app/src/main/res/layout/activity_important_information.xml +++ b/app/src/main/res/layout/activity_important_information.xml @@ -31,10 +31,23 @@ tools:context="com.g11x.checklistapp.ImportantInformationActivity" android:orientation="horizontal"> + + + android:layout_height="match_parent" + android:visibility="gone"/> + style="@style/AppCardViewStyle" + android:id="@+id/card_view"> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_navigation.xml b/app/src/main/res/layout/activity_navigation.xml deleted file mode 100644 index f1ca44d..0000000 --- a/app/src/main/res/layout/activity_navigation.xml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_notification_list.xml b/app/src/main/res/layout/activity_notification_list.xml index 25c3858..d129d11 100644 --- a/app/src/main/res/layout/activity_notification_list.xml +++ b/app/src/main/res/layout/activity_notification_list.xml @@ -1,15 +1,15 @@ - + - + \ No newline at end of file diff --git a/app/src/main/res/layout/view_checklist_item.xml b/app/src/main/res/layout/view_checklist_item.xml index e8f98a1..4986fb9 100644 --- a/app/src/main/res/layout/view_checklist_item.xml +++ b/app/src/main/res/layout/view_checklist_item.xml @@ -16,18 +16,15 @@ ~ --> - + style="@style/AppCardViewStyle" + android:id="@+id/card_view"> - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_important_information_item.xml b/app/src/main/res/layout/view_important_information_item.xml index b8dc914..497bb6e 100644 --- a/app/src/main/res/layout/view_important_information_item.xml +++ b/app/src/main/res/layout/view_important_information_item.xml @@ -16,14 +16,15 @@ ~ --> - + + - \ No newline at end of file + style="@style/AppCardViewTitleTextStyle"/> + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_notification_item.xml b/app/src/main/res/layout/view_notification_item.xml index 04e48d5..22fcc47 100644 --- a/app/src/main/res/layout/view_notification_item.xml +++ b/app/src/main/res/layout/view_notification_item.xml @@ -15,35 +15,29 @@ ~ limitations under the License. ~ --> - + + - - + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="5dp"> - + - - - + + + diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 258c7d8..b49f0ac 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -29,7 +29,8 @@ Create Created %s Список заданий - Персональные данные + Заметки + Уведомления Выбрать язык О программе diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index f8c67c9..396eca5 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -30,7 +30,8 @@ Create Created %s Список завдань - Персональні дані + Нотатки + Повідомлення Змінити мову Про програму diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9e52c6d..8a1cda6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -30,7 +30,7 @@ Create Created %s Checklist - Private + Notes Notifications Language About @@ -40,4 +40,5 @@ Settings Content not yet loaded + Put important info here... diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 8d2b1d5..de8efb6 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -25,4 +25,22 @@ @color/accent + + + + diff --git a/app/testdata/checklistappdev-export.json b/app/testdata/checklistappdev-export.json index d90948e..b8f810b 100644 --- a/app/testdata/checklistappdev-export.json +++ b/app/testdata/checklistappdev-export.json @@ -1,46 +1,76 @@ -[ null, { - "alt_langs" : { - "pr" : { - "description" : "Portugese description", - "name" : "Portugese name" - }, - "ru" : { - "description" : "Russian description", - "name" : "Russian name" - } - }, - "description" : "In the United States, a Social Security number is a number assigned to U.S. Citizens, permanent residents, and temporary (working) residents. The primary purpose is to track individuals for taxes and identification. A Social Security lists your name and Social Security number, and is often necessary for obtaining other items, such as a state identification card. People don't usually carry their Social Security cards with them everywhere, since they are not usually necessary on a day-to-day basis.", - "directionsUrl" : "https://www.google.com/maps/place/US+Social+Security+Administration/@30.3243715,-97.7028468,17z/data=!3m1!4b1!4m5!3m4!1s0x8644ca20295a7e57:0x849908e001c1d623!8m2!3d30.3243715!4d-97.7006581", - "email" : "", - "location" : "US Social Security Administration, 1029 Camino La Costa, Austin, TX 78752", - "name" : "Get Social Security card", - "phone" : "1-800-772-1213" -}, { - "description" : "State identification cards, or state ID cards, are used to identify people for everyday tasks such as financial transactions and age verification. In order to obtain a state ID card in Texas, you will need a Social Security card. You should carry your state ID card with you every day.", - "directionsUrl" : "https://www.google.com/maps/place/Texas+Department+of+Public+Safety/@30.3264658,-97.7255281,17z/data=!3m1!4b1!4m5!3m4!1s0x8644ca46dcf0be55:0x1a342c9d9863d05b!8m2!3d30.3264658!4d-97.7233394", - "email" : "", - "location" : "Texas Department of Public Safety 5805 N Lamar Blvd, Austin, TX 78752", - "name" : "Get state identification card", - "phone" : "1-512-424-2000" -}, { - "description" : "In the United States, if you are looking for employment, but do not currently have a job, you can often collect 'unemployment benefits' for a short period of time. These payments are provided by the state government, and they help you pay for basic needs until you have a job to support yourself.", - "directionsUrl" : "https://www.google.com/maps/place/Texas+Workforce+Commission/@30.2819065,-97.7135026,17z/data=!3m1!4b1!4m5!3m4!1s0x8644b5e91d5120d7:0x2fe0ab2c19a88990!8m2!3d30.2819065!4d-97.7113139", - "email" : "laborinfo@twc.state.tx.us", - "location" : "Texas Workforce Commission 2810 E Martin Luther King Jr Blvd, Austin, TX 78702", - "name" : "Submit information to unemployment office", - "phone" : "1-512-480-8101" -}, { - "description" : "The Capital Metro system provides public transportation via bus and sometimes light rail anywhere within Austin city limits. When you board a bus or train, you will be required to either pay for the ride, or show a bus pass, so you should carry a bus pass with you at all times.", - "directionsUrl" : "https://www.google.com/maps/place/Capital+Metro+Transit+Store/@30.271223,-97.7462346,17z/data=!3m1!4b1!4m5!3m4!1s0x8644b508420e242d:0x8df332980f4b004b!8m2!3d30.271223!4d-97.7440459", - "email" : "customer.service@capmetro.org", - "location" : "Capital Metro 209 West 9th Street, Austin, TX 78701", - "name" : "Buy a bus pass", - "phone" : " 1-512-389-7454" -}, { - "description" : "In the United States, public school is provided for children from 5 to 18 years of age. In Austin, the school year runs from September until June. In order to attend public school, children must be registered at the Austin Independent School District office in downtown Austin.", - "directionsUrl" : "https://www.google.com/maps/place/Austin+Independent+School+District/@30.271564,-97.7587266,17z/data=!3m1!4b1!4m5!3m4!1s0x8644b513a2be5731:0xb4f1cb61acd29991!8m2!3d30.271564!4d-97.7565379", - "email" : "", - "location" : "Austin Independent School District 1111 W 6th Street Austin, TX 78703", - "name" : "Enroll children in school", - "phone" : "1-512-414-1700" -} ] +{ + "checklists": { + "basic": [ + { + "description": "In the United States, a Social Security number is a number assigned to U.S. Citizens, permanent residents, and temporary (working) residents. The primary purpose is to track individuals for taxes and identification. A Social Security lists your name and Social Security number, and is often necessary for obtaining other items, such as a state identification card. People don't usually carry their Social Security cards with them everywhere, since they are not usually necessary on a day-to-day basis.", + "directionsUrl": "https://www.google.com/maps/place/US+Social+Security+Administration/@30.3243715,-97.7028468,17z/data=!3m1!4b1!4m5!3m4!1s0x8644ca20295a7e57:0x849908e001c1d623!8m2!3d30.3243715!4d-97.7006581", + "email": "", + "location": "US Social Security Administration, 1029 Camino La Costa, Austin, TX 78752", + "name": "Get Social Security card", + "phone": "1-800-772-1213", + "alt": { + "uk": { + "description": "фвіло а відліфдлафі фідлвдао Дфдівлцшут Флфвдлар млвлщіот а вілід івшів ідд ашуцтцт м іщфщяжзсь вшщфщ утуіщщц утвлсмщщм уцутіщшвщіва вітівлвіщшівар автмщмщлм мфшшщ ц цутуц щш тів івщіщівщшугу утклс мшм т цщу м щю л щ фі шч а тущ цтуцда ащшв шіщоіщ авіщі луц цущ омьжвфілдві щшвідлів щццутлал щш аівтвіадл ю", + "name": "длоівлд вділівдл ві іт іддіввлу" + } + } + }, + { + "description": "State identification cards, or state ID cards, are used to identify people for everyday tasks such as financial transactions and age verification. In order to obtain a state ID card in Texas, you will need a Social Security card. You should carry your state ID card with you every day.", + "directionsUrl": "https://www.google.com/maps/place/Texas+Department+of+Public+Safety/@30.3264658,-97.7255281,17z/data=!3m1!4b1!4m5!3m4!1s0x8644ca46dcf0be55:0x1a342c9d9863d05b!8m2!3d30.3264658!4d-97.7233394", + "email": "", + "location": "Texas Department of Public Safety 5805 N Lamar Blvd, Austin, TX 78752", + "name": "Get state identification card", + "phone": "1-512-424-2000", + "alt": { + "uk": { + "description": "фівлода цшщку оьдцу щшмів тоукцшщтвм щшвіиащшцутошщ йатумрдщгуцжолукптом сщш івф щфцрок шгщзущгу тф івофвт ф івшщтло аф фщшук оуцфрош івгавіощ лтолур гуцагрцтщу г9уцагріоато лів шиіг шщ цуі цщшцу щшцудл тл оіва шщівт олац ошшгцутцут ацщш цшщгщ ц =цутрт", + "name": "лдфівлдо цуолдук длвідлів лдтцу" + } + } + }, + { + "description": "In the United States, if you are looking for employment, but do not currently have a job, you can often collect 'unemployment benefits' for a short period of time. These payments are provided by the state government, and they help you pay for basic needs until you have a job to support yourself.", + "directionsUrl": "https://www.google.com/maps/place/Texas+Workforce+Commission/@30.2819065,-97.7135026,17z/data=!3m1!4b1!4m5!3m4!1s0x8644b5e91d5120d7:0x2fe0ab2c19a88990!8m2!3d30.2819065!4d-97.7113139", + "email": "laborinfo@twc.state.tx.us", + "location": "Texas Workforce Commission 2810 E Martin Luther King Jr Blvd, Austin, TX 78702", + "name": "Submit information to unemployment office", + "phone": "1-512-480-8101", + "alt": { + "uk": { + "description": "фліваофлц укфцлджф щфівтолцу ктшщітвф имрн пцуйийуцоштп гз9гшцуийцу пщшоцшгз міфмвш згцтош фшцз ромфош злдіфдвла щцфулофлжаоіш фцуатоиукифвмщозфігшв оимврщшг іфв отфт цоудк ошфуцаотофвіофгшцуротфцу аиофц уоало іфвадл орфівао фцрофцутлфолфлтфівмло фівол олфіважлдфів алоцуоттцуитмв", + "name": "фоліва лфілова олфцуолф орфуцдоллфцолуруц" + } + } + }, + { + "description": "The Capital Metro system provides public transportation via bus and sometimes light rail anywhere within Austin city limits. When you board a bus or train, you will be required to either pay for the ride, or show a bus pass, so you should carry a bus pass with you at all times.", + "directionsUrl": "https://www.google.com/maps/place/Capital+Metro+Transit+Store/@30.271223,-97.7462346,17z/data=!3m1!4b1!4m5!3m4!1s0x8644b508420e242d:0x8df332980f4b004b!8m2!3d30.271223!4d-97.7440459", + "email": "customer.service@capmetro.org", + "location": "Capital Metro 209 West 9th Street, Austin, TX 78701", + "name": "Buy a bus pass", + "phone": " 1-512-389-7454", + "alt": { + "uk": { + "description": "фівж лолцдоу цуорфпшг щриіщ шзячшщо влі згщяіхщ0-щз 2ойлу аовіфшгрцоршй2и3ршггшрвіфмиофів гіфвощцоуфгшафщфіуо іфошврао віфт цуирк йзшгцуреииф зівирфівршпщгцфуркицуфгшзіу фуоифцушозафз шцгуа ифцуаоштцу гзцуфграшифуцкеизш фпфц иуирфуцуфоа фцщц нпуцфируцфдцуф гг9уцф пифптфіщшшяващя ивгяівгуци цуйий", + "name": "лофі лфцутлждкцуф жолд лів тоцфу одшуцо куц" + } + } + }, + { + "description": "In the United States, public school is provided for children from 5 to 18 years of age. In Austin, the school year runs from September until June. In order to attend public school, children must be registered at the Austin Independent School District office in downtown Austin.", + "directionsUrl": "https://www.google.com/maps/place/Austin+Independent+School+District/@30.271564,-97.7587266,17z/data=!3m1!4b1!4m5!3m4!1s0x8644b513a2be5731:0xb4f1cb61acd29991!8m2!3d30.271564!4d-97.7565379", + "email": "", + "location": "Austin Independent School District 1111 W 6th Street Austin, TX 78703", + "name": "Enroll children in school", + "phone": "1-512-414-1700", + "alt": { + "uk": { + "description": "фол жлдуцфк олфуц олфвіолп ігшзщфутцл офру ощкуц щшофу і пфіо офіолажфіжл алофцургшпиукф ддлтфіпщшфуц офтулц жлфіу щофуі офутлодуіжлфо цуощшфцуолфцутифуцоажфцу отфцуорфоц удолифуо фуцорфу тфцуло жжуо ффтдлуіди оуфікпзгрфкпш ортіолмтяогяшруіо пдлфол пов аофіо фвілодаорф ушще уолфддоавл", + "name": "олфі л ілді цлроу" + } + } + } + ] + } +} diff --git a/build.gradle b/build.gradle index 1a16d93..242e4d1 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.0-beta1' + classpath 'com.android.tools.build:gradle:2.2.0-beta2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files