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/AboutActivity.java b/app/src/main/java/com/g11x/checklistapp/AboutActivity.java index be72b34..ba6784e 100644 --- a/app/src/main/java/com/g11x/checklistapp/AboutActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/AboutActivity.java @@ -30,7 +30,6 @@ protected int getNavDrawerItemIndex() { @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); setContentView(R.layout.activity_about); diff --git a/app/src/main/java/com/g11x/checklistapp/AppPreferences.java b/app/src/main/java/com/g11x/checklistapp/AppPreferences.java index 0d71a14..516e18f 100644 --- a/app/src/main/java/com/g11x/checklistapp/AppPreferences.java +++ b/app/src/main/java/com/g11x/checklistapp/AppPreferences.java @@ -56,7 +56,7 @@ public static void setLanguageOverride(@NonNull Context context, @Nullable Language language) { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = sp.edit(); - if (language == null) { + if (language == null || language.equals(Language.SystemDefault)) { editor.remove(PREF_PREFERRED_LANGUAGE); } else { editor.putString(PREF_PREFERRED_LANGUAGE, language.name()); @@ -65,7 +65,7 @@ public static void setLanguageOverride(@NonNull Context context, } /** - * Returns the language override or null when not set. + * Returns the language override setting. * * @param context component context used to retrieve shared preferences */ @@ -74,7 +74,7 @@ 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.SystemDefault; } return Language.valueOf(value); } diff --git a/app/src/main/java/com/g11x/checklistapp/ChecklistActivity.java b/app/src/main/java/com/g11x/checklistapp/ChecklistActivity.java index 2f649ff..7c0b8f3 100644 --- a/app/src/main/java/com/g11x/checklistapp/ChecklistActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/ChecklistActivity.java @@ -18,6 +18,8 @@ package com.g11x.checklistapp; import android.content.Intent; +import android.database.Cursor; +import android.graphics.Paint; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -26,26 +28,37 @@ 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 DatabaseReference databaseRef; + private AppPreferences.LanguageChangeListener languageChangeListener; + private Language language; @Override protected int getNavDrawerItemIndex() { return NavigationActivity.NAVDRAWER_ITEM_CHECKLIST; } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_checklist); - databaseRef = FirebaseDatabase.getInstance().getReference() + 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"); @@ -57,10 +70,16 @@ protected void onCreate(Bundle savedInstanceState) { ChecklistItem.class, R.layout.view_checklist_item, ChecklistItemHolder.class, databaseRef) { + @Override + public void onBindViewHolder(ChecklistItemHolder viewHolder, int position) { + super.onBindViewHolder(viewHolder, 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) { @@ -77,22 +96,44 @@ public void onClick(View view) { @Override protected void onStart() { super.onStart(); + // Need to populate done status at onStart as well. + Cursor donenessCursor = getContentResolver().query(Database.ChecklistItem.CONTENT_URI, + ChecklistItem.PROJECTION, null, null, null); + if (donenessCursor.moveToFirst()) { + for (int i = 0; i < donenessCursor.getCount(); i++) { + String itemHash = donenessCursor.getString(ChecklistItem.ITEM_HASH_COLUMN_INDEX); + int done = donenessCursor.getInt(ChecklistItem.DONE_COLUMN_INDEX); + donenessCursor.moveToNext(); + } + } + } + + 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); view = itemView; } + public void markDone(boolean done) { + if (done) { + TextView textView = (TextView) view.findViewById(R.id.info_text); + textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); + } + } + public void setText(String title) { TextView textView = (TextView) view.findViewById(R.id.info_text); textView.setText(title); diff --git a/app/src/main/java/com/g11x/checklistapp/ChecklistItemActivity.java b/app/src/main/java/com/g11x/checklistapp/ChecklistItemActivity.java index 5e35500..11d2971 100644 --- a/app/src/main/java/com/g11x/checklistapp/ChecklistItemActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/ChecklistItemActivity.java @@ -17,6 +17,7 @@ package com.g11x.checklistapp; +import android.content.ContentResolver; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; @@ -27,7 +28,7 @@ import android.widget.ToggleButton; import com.g11x.checklistapp.data.ChecklistItem; -import com.g11x.checklistapp.data.ChecklistManager; +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,6 +40,8 @@ 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) { @@ -46,7 +49,16 @@ protected void onCreate(Bundle 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() { @@ -64,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() { @@ -80,7 +102,7 @@ public void onClick(View view) { }); isDone = (ToggleButton) findViewById(R.id.doneness); - isDone.setChecked(checklistItem.isDone()); + isDone.setChecked(checklistItem.isDone(getContentResolver())); isDone.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { @@ -89,14 +111,9 @@ public void onClick(View view) { }); } - @Override - protected void onStart() { - super.onStart(); - } - private void onClickIsDone() { - checklistItem.setDone(!checklistItem.isDone()); - ChecklistManager.save(getApplicationContext(), ChecklistManager.get(getApplicationContext())); + ContentResolver contentResolver = getContentResolver(); + checklistItem.setDone(contentResolver, !checklistItem.isDone(contentResolver)); } private void onClickGetDirections() { diff --git a/app/src/main/java/com/g11x/checklistapp/LanguageActivity.java b/app/src/main/java/com/g11x/checklistapp/LanguageActivity.java index 074cca8..c6d2ce7 100644 --- a/app/src/main/java/com/g11x/checklistapp/LanguageActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/LanguageActivity.java @@ -18,6 +18,7 @@ package com.g11x.checklistapp; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.CardView; @@ -42,7 +43,9 @@ protected void onCreate(Bundle savedInstanceState) { RecyclerView recyclerView = (RecyclerView) findViewById(R.id.main_content); recyclerView.setLayoutManager(new LinearLayoutManager(this)); - recyclerView.setAdapter(new LanguageSelectionAdapter()); + Language language = AppPreferences.getLanguageOverride(this); + recyclerView.setAdapter(new LanguageSelectionAdapter(language)); + } @Override @@ -52,6 +55,12 @@ protected int getNavDrawerItemIndex() { static class LanguageSelectionAdapter extends RecyclerView.Adapter { private Language[] dataSet = Language.values(); + @NonNull + private final Language language; + + LanguageSelectionAdapter(@NonNull Language lang) { + language = lang; + } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { @@ -64,6 +73,14 @@ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public void onBindViewHolder(ViewHolder holder, int position) { holder.language = dataSet[position]; holder.textView.setText(dataSet[position].getNativeDescription()); + if (language.equals(holder.language)) { + holder.cardView.setSelected(true); + holder.cardView.setActivated(true); + holder.cardView.setCardBackgroundColor(holder.cardView.getResources().getColor(R.color.accent)); + } else { + holder.cardView.setSelected(false); + holder.cardView.setActivated(false); + } } @Override @@ -74,8 +91,10 @@ public int getItemCount() { class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private TextView textView; private Language language; + private CardView cardView; ViewHolder(final CardView view) { super(view); + cardView = view; textView = (TextView)view.findViewById(R.id.checkbox); view.setOnClickListener(this); } diff --git a/app/src/main/java/com/g11x/checklistapp/LocalRepository.java b/app/src/main/java/com/g11x/checklistapp/LocalRepository.java index d8f1a29..6099117 100644 --- a/app/src/main/java/com/g11x/checklistapp/LocalRepository.java +++ b/app/src/main/java/com/g11x/checklistapp/LocalRepository.java @@ -46,17 +46,22 @@ public String getType(@NonNull Uri uri) { @Nullable @Override public Uri insert(@NonNull Uri uri, ContentValues contentValues) { - return Database.insert(openHelper.getWritableDatabase(), uri, contentValues); + Uri insertUri = Database.insert(openHelper.getWritableDatabase(), uri, contentValues); + getContext().getContentResolver().notifyChange(uri, null); + getContext().getContentResolver().notifyChange(insertUri, null); + return insertUri; } @Override public int delete(@NonNull Uri uri, String s, String[] strings) { - return 0; + throw new UnsupportedOperationException(); } @Override public int update(@NonNull Uri uri, ContentValues contentValues, String selection, String[] selectionArgs) { - return Database.update(openHelper.getWritableDatabase(), uri, contentValues, selection, selectionArgs); + int updateValue = Database.update(openHelper.getWritableDatabase(), uri, contentValues, selection, selectionArgs); + getContext().getContentResolver().notifyChange(uri, null); + return updateValue; } // Helper class that actually creates and manages the provider's underlying data repository. diff --git a/app/src/main/java/com/g11x/checklistapp/NavigationActivity.java b/app/src/main/java/com/g11x/checklistapp/NavigationActivity.java index 586e1fa..3843412 100644 --- a/app/src/main/java/com/g11x/checklistapp/NavigationActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/NavigationActivity.java @@ -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; @@ -44,11 +45,11 @@ public abstract class NavigationActivity extends AppCompatActivity { private DrawerLayout drawerLayout; private ListView drawerList; private ActionBarDrawerToggle drawerToggle; - private AppPreferences.LanguageChangeListener languageChangeListener; + 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; @@ -70,7 +71,7 @@ protected void onCreate(Bundle savedInstanceState) { overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); PreferredLanguageSupport.applyPreferredLanguage(this); final Activity thisActivity = NavigationActivity.this; - languageChangeListener = new AppPreferences.LanguageChangeListener(NavigationActivity.this) { + baseLanguageChangeListener = new AppPreferences.LanguageChangeListener(NavigationActivity.this) { @Override public void onChanged(String newValue) { thisActivity.recreate(); @@ -84,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()); @@ -132,7 +133,7 @@ public void onDrawerOpened(View drawerView) { @Override protected void onDestroy() { drawerLayout.removeDrawerListener(drawerToggle); - languageChangeListener.unregister(this); + baseLanguageChangeListener.unregister(this); super.onDestroy(); } @@ -144,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) { @@ -187,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); + } } /** @@ -204,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); } @@ -212,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..e83ab30 100644 --- a/app/src/main/java/com/g11x/checklistapp/NotificationListActivity.java +++ b/app/src/main/java/com/g11x/checklistapp/NotificationListActivity.java @@ -1,17 +1,31 @@ package com.g11x.checklistapp; +import android.content.ContentValues; +import android.database.ContentObserver; +import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; +import android.os.Handler; +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; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; 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 { +public class NotificationListActivity extends NavigationActivity implements LoaderManager.LoaderCallbacks { + + private NotificationAdapter adapter; + private RecyclerView mRecyclerView; + private ContentObserver notificationContentObserver; @Override protected int getNavDrawerItemIndex() { @@ -22,7 +36,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 +54,86 @@ 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); + adapter = new NotificationAdapter(null); + mRecyclerView.setAdapter(adapter); - // 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); + getSupportLoaderManager().initLoader(0, null, this); } - public class NotificationAdapter extends RecyclerView.Adapter { - private final Notification[] mDataset; + @Override + protected void onStart() { + super.onStart(); + if (adapter != null) { + adapter.notifyDataSetChanged(); + } + } - // 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 Loader onCreateLoader(int i, Bundle bundle) { + if (notificationContentObserver != null) { + getContentResolver().unregisterContentObserver(notificationContentObserver); + } + notificationContentObserver = new ContentObserver(new Handler()) { + @Override + public void onChange(boolean selfChange) { + getSupportLoaderManager().restartLoader(0, null, NotificationListActivity.this); + super.onChange(selfChange); + } - 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 onChange(boolean selfChange, Uri uri) { + getSupportLoaderManager().restartLoader(0, null, NotificationListActivity.this); + super.onChange(selfChange, uri); } - } + }; + Loader cursor = new CursorLoader(NotificationListActivity.this, + Database.Notification.CONTENT_URI, + Database.Notification.PROJECTION, "NOT " + Database.Notification.READ_COLUMN, null, Database.Notification.SENT_TIME); + getContentResolver().registerContentObserver(Database.Notification.CONTENT_URI, true, notificationContentObserver); + return cursor; + } + + @Override + public void onLoadFinished(Loader loader, Cursor cursor) { + adapter.swapCursor(cursor); + } + + @Override + public void onLoaderReset(Loader loader) { + adapter.swapCursor(null); + } + + 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 +144,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 57a04f8..1450a9e 100644 --- a/app/src/main/java/com/g11x/checklistapp/data/ChecklistItem.java +++ b/app/src/main/java/com/g11x/checklistapp/data/ChecklistItem.java @@ -17,10 +17,18 @@ package com.g11x.checklistapp.data; +import android.content.ContentResolver; +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; @@ -30,36 +38,99 @@ public class ChecklistItem { private String email; private String phone; private Uri directions; + private String hash; + + public static final String[] PROJECTION = { + Database.ID_COLUMN, + Database.ChecklistItem.ITEM_HASH_COLUMN, + Database.ChecklistItem.DONE_COLUMN, + }; + 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) } - private ChecklistItem(String name, String description, boolean isDone, String location, String directionsUrl, String email, String phone) { + private ChecklistItem( + String name, String description, String location, String directionsUrl, String email, + String phone) { this.name = name; this.description = description; - this.isDone = isDone; this.location = location; this.directionsUrl = directionsUrl; this.email = email; this.phone = phone; } + @SuppressWarnings("WeakerAccess") public String getName() { return name; } + private Cursor getCursorForItem(ContentResolver contentResolver) { + return contentResolver.query( + Database.ChecklistItem.CONTENT_URI, + ChecklistItem.PROJECTION, + Database.ChecklistItem.ITEM_HASH_COLUMN + " = ?", + 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 void setDone(boolean done) { - isDone = done; + public boolean isDone(ContentResolver contentResolver) { + Cursor cursor = getCursorForItem(contentResolver); + int done = 0; + if (cursor.moveToFirst()) { + done = cursor.getInt(ChecklistItem.DONE_COLUMN_INDEX); + } + return done != 0; } - public static ChecklistItem of(String name, String description, boolean isDone, String location, Uri directions, String email, String phone) { - return new ChecklistItem(name, description, isDone, location, directions.toString(), email, phone); + public void setDone(ContentResolver contentResolver, boolean done) { + ContentValues newValues = new ContentValues(); + newValues.put(Database.ChecklistItem.ITEM_HASH_COLUMN, getHash()); + newValues.put(Database.ChecklistItem.DONE_COLUMN, done); + + Cursor cursor = getCursorForItem(contentResolver); + + if (cursor.moveToFirst()) { + contentResolver.update(Database.ChecklistItem.CONTENT_URI, newValues, + Database.ChecklistItem.ITEM_HASH_COLUMN + " = ?", + new String[] { getHash() } + ); + } else { + contentResolver.insert(Database.ChecklistItem.CONTENT_URI, newValues); + } } @SuppressWarnings("unused") @@ -77,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; @@ -92,4 +188,16 @@ public Uri getDirections() { } return directions; } + + public String getHash() { + if (hash == null) { + hash = String.valueOf(name.hashCode()); + } + return hash; + } + + @SuppressWarnings("unused") + public Map> getAlt() { + return alt; + } } diff --git a/app/src/main/java/com/g11x/checklistapp/data/ChecklistManager.java b/app/src/main/java/com/g11x/checklistapp/data/ChecklistManager.java deleted file mode 100644 index 832ba9c..0000000 --- a/app/src/main/java/com/g11x/checklistapp/data/ChecklistManager.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.data; - -import android.content.Context; -import android.content.SharedPreferences; -import android.net.Uri; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -public class ChecklistManager { - private static Checklist checklist; - - public static Checklist get(Context context) { - if (checklist== null) { - SharedPreferences preferences = context.getSharedPreferences("checklist", Context.MODE_PRIVATE); - String items = preferences.getString("doneness", null); - - if (items == null) { - checklist = fromDefault(); - } else { - ArrayList doneness = new ArrayList<>(); - - for (String item : items.split(",")) { - doneness.add(Boolean.valueOf(item)); - } - - checklist = fromDoneness(doneness); - } - } - - return checklist; - } - - public static void save(Context context, Checklist checklist) { - StringBuilder doneness = new StringBuilder(); - for (ChecklistItem item : checklist.getItems()) { - doneness.append(String.valueOf(item.isDone())).append(","); - } - SharedPreferences settings = context.getSharedPreferences("checklist", Context.MODE_PRIVATE); - SharedPreferences.Editor editor = settings.edit(); - editor.putString("doneness", doneness.toString()); - editor.apply(); - } - - private static Checklist fromDefault() { - return fromDoneness(Arrays.asList(false, false, false, false, false)); - } - - private static Checklist fromDoneness(List doneness) { - return Checklist.of(Arrays.asList( - ChecklistItem.of("Get social security card", - "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.", - doneness.get(0), - "US Social Security Administration, 1029 Camino La Costa, Austin, TX 78752", - Uri.parse("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"), - null, - "1-800-772-1213"), - ChecklistItem.of("Get state identification card", - "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.", - doneness.get(1), - "Texas Department of Public Safety 5805 N Lamar Blvd, Austin, TX 78752", - Uri.parse("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"), - null, - "1-512-424-2000"), - ChecklistItem.of("Submit information to unemployment office", - "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.", - doneness.get(2), - "Texas Workforce Commission 2810 E Martin Luther King Jr Blvd, Austin, TX 78702", - Uri.parse("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"), - "laborinfo@twc.state.tx.us", "1-512-480-8101"), - ChecklistItem.of("Buy a bus pass", - "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.", - doneness.get(3), - "Capital Metro 209 West 9th Street, Austin, TX 78701", - Uri.parse("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"), - "customer.service@capmetro.org", " 1-512-389-7454"), - ChecklistItem.of("Enroll children in school", - "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.", - doneness.get(4), - "Austin Independent School District 1111 W 6th Street Austin, TX 78703", - Uri.parse("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"), - null, - "1-512-414-1700"))); - } -} 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 0d9b917..eba6c4a 100644 --- a/app/src/main/java/com/g11x/checklistapp/data/Database.java +++ b/app/src/main/java/com/g11x/checklistapp/data/Database.java @@ -148,8 +148,10 @@ public Uri insert(ContentValues contentValues) { } @Override - public Cursor query(String[] projection, String selection, String[] selectionArgs, String sortOrder) { - return null; + public Cursor query( + String[] projection, String selection, String[] selectionArgs, String sortOrder) { + return db.query(ChecklistItem.TABLE_NAME, projection, selection, selectionArgs, null, null, + sortOrder); } @Override @@ -176,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. @@ -196,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 @@ -213,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 index 1587dca..bc5921b 100644 --- a/app/src/main/java/com/g11x/checklistapp/language/Language.java +++ b/app/src/main/java/com/g11x/checklistapp/language/Language.java @@ -23,6 +23,7 @@ * Custom content languages supported by the app. */ public enum Language { + SystemDefault(null, "System Default"), Russian("ru", "Русский"), English("en", "English"), Ukrainian("uk", "Українська"); diff --git a/app/src/main/java/com/g11x/checklistapp/language/PreferredLanguageSupport.java b/app/src/main/java/com/g11x/checklistapp/language/PreferredLanguageSupport.java index 5ac841a..63fb1a9 100644 --- a/app/src/main/java/com/g11x/checklistapp/language/PreferredLanguageSupport.java +++ b/app/src/main/java/com/g11x/checklistapp/language/PreferredLanguageSupport.java @@ -35,7 +35,7 @@ public class PreferredLanguageSupport { public static void applyPreferredLanguage(@NonNull Context context) { Language language = AppPreferences.getLanguageOverride(context); - if (language != null) { + if (language != null && language != Language.SystemDefault) { applyPreferredDefaultLanguage(context, language); } else { unsetPreferredDefaultLanguage(context); 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_language_item.xml b/app/src/main/res/layout/activity_language_item.xml index 9cc0886..2b12eec 100644 --- a/app/src/main/res/layout/activity_language_item.xml +++ b/app/src/main/res/layout/activity_language_item.xml @@ -18,6 +18,7 @@ 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 1086b1d..d129d11 100644 --- a/app/src/main/res/layout/activity_notification_list.xml +++ b/app/src/main/res/layout/activity_notification_list.xml @@ -5,11 +5,11 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + - + \ No newline at end of file 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