diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 6961a30..1c760bd 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -3,7 +3,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 3c6fdb2..4c0868c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,11 +1,18 @@ apply plugin: 'com.android.application' android { + signingConfigs { + config { + keyAlias 'Bucket Drops' + keyPassword 'appCOURSE' + storeFile file('/Users/vivz/Desktop/Examples/AndroidExamples/BucketDrops/bucketdrops.jks') + storePassword 'bucketDROPS' + } + } compileSdkVersion 23 buildToolsVersion "23.0.2" - defaultConfig { - applicationId "slidenerd.vivz.bucketdrops" + applicationId "appcourse.vivz.bucketdrops" minSdkVersion 15 targetSdkVersion 23 versionCode 1 @@ -13,15 +20,21 @@ android { } buildTypes { release { - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.config + } + debug { } } } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) + compile fileTree(include: ['*.jar'], dir: 'libs') testCompile 'junit:junit:4.12' - compile 'com.android.support:appcompat-v7:23.1.1' + compile 'com.android.support:appcompat-v7:23.2.0' compile 'com.github.bumptech.glide:glide:3.6.1' + compile 'io.realm:realm-android:0.87.5' + compile 'com.android.support:recyclerview-v7:23.2.0' + compile 'com.github.halysongoncalves:pugnotification:1.8.1' } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 263c64e..6b8024a 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -15,3 +15,18 @@ #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} +-keep class io.realm.annotations.RealmModule +-keep @io.realm.annotations.RealmModule class * +-keep class io.realm.internal.Keep +-keep @io.realm.internal.Keep class * { *; } +-dontwarn javax.** +-dontwarn io.realm.** +-keep public class * implements com.bumptech.glide.module.GlideModule +-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** { + **[] $VALUES; + public *; +} +-dontwarn com.squareup.okhttp.** +-dontwarn android.support.v7.** +-keep class android.support.v7.** { *; } +-keep interface android.support.v7.** { *; } \ No newline at end of file diff --git a/app/src/androidTest/java/slidenerd/vivz/bucketdrops/ApplicationTest.java b/app/src/androidTest/java/appcourse/vivz/bucketdrops/ApplicationTest.java similarity index 89% rename from app/src/androidTest/java/slidenerd/vivz/bucketdrops/ApplicationTest.java rename to app/src/androidTest/java/appcourse/vivz/bucketdrops/ApplicationTest.java index bae4238..fe8281e 100644 --- a/app/src/androidTest/java/slidenerd/vivz/bucketdrops/ApplicationTest.java +++ b/app/src/androidTest/java/appcourse/vivz/bucketdrops/ApplicationTest.java @@ -1,4 +1,4 @@ -package slidenerd.vivz.bucketdrops; +package appcourse.vivz.bucketdrops; import android.app.Application; import android.test.ApplicationTestCase; diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b2fdab1..1bebfea 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,20 +1,34 @@ + package="appcourse.vivz.bucketdrops"> - + + + + + + + + + diff --git a/app/src/main/assets/fonts/raleway_thin.ttf b/app/src/main/assets/fonts/raleway_thin.ttf new file mode 100755 index 0000000..dcc9e4f Binary files /dev/null and b/app/src/main/assets/fonts/raleway_thin.ttf differ diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/ActivityMain.java b/app/src/main/java/appcourse/vivz/bucketdrops/ActivityMain.java new file mode 100644 index 0000000..f9e7b2c --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/ActivityMain.java @@ -0,0 +1,211 @@ +package appcourse.vivz.bucketdrops; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.Toolbar; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; + +import com.bumptech.glide.Glide; + +import io.realm.Realm; +import io.realm.RealmChangeListener; +import io.realm.RealmResults; +import io.realm.Sort; +import appcourse.vivz.bucketdrops.adapters.AdapterDrops; +import appcourse.vivz.bucketdrops.adapters.AddListener; +import appcourse.vivz.bucketdrops.adapters.CompleteListener; +import appcourse.vivz.bucketdrops.adapters.Divider; +import appcourse.vivz.bucketdrops.adapters.Filter; +import appcourse.vivz.bucketdrops.adapters.MarkListener; +import appcourse.vivz.bucketdrops.adapters.ResetListener; +import appcourse.vivz.bucketdrops.adapters.SimpleTouchCallback; +import appcourse.vivz.bucketdrops.beans.Drop; +import appcourse.vivz.bucketdrops.extras.Util; +import appcourse.vivz.bucketdrops.widgets.BucketRecyclerView; + +public class ActivityMain extends AppCompatActivity { + + public static final String TAG_DIALOG_ADD = "add"; + public static final String TAG_DIALOG_MARK = "mark"; + public static final String FIELD_WHEN = "when"; + public static final String FIELD_COMPLETED = "completed"; + Toolbar mToolbar; + Button mBtnAdd; + BucketRecyclerView mRecycler; + Realm mRealm; + RealmResults mResults; + View mEmptyView; + AdapterDrops mAdapter; + private View.OnClickListener mBtnAddListener = new View.OnClickListener() { + + @Override + public void onClick(View v) { + showDialogAdd(); + } + }; + private AddListener mAddListener = new AddListener() { + @Override + public void add() { + showDialogAdd(); + } + }; + + private RealmChangeListener mChangeListener = new RealmChangeListener() { + @Override + public void onChange() { + mAdapter.update(mResults); + } + }; + + private MarkListener mMarkListener = new MarkListener() { + @Override + public void onMark(int position) { + showDialogMark(position); + } + }; + + private CompleteListener mCompleteListener = new CompleteListener() { + @Override + public void onComplete(int position) { + mAdapter.markComplete(position); + } + }; + private ResetListener mResetListener = new ResetListener() { + @Override + public void onReset() { + AppBucketDrops.save(ActivityMain.this, Filter.NONE); + loadResults(Filter.NONE); + } + }; + + private void showDialogAdd() { + DialogAdd dialog = new DialogAdd(); + dialog.show(getSupportFragmentManager(), TAG_DIALOG_ADD); + } + + private void showDialogMark(int position) { + DialogMark dialog = new DialogMark(); + Bundle bundle = new Bundle(); + bundle.putInt(DialogMark.EXTRA_POSITION, position); + dialog.setArguments(bundle); + dialog.setCompleteListener(mCompleteListener); + dialog.show(getSupportFragmentManager(), TAG_DIALOG_MARK); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + mRealm = Realm.getDefaultInstance(); + mToolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(mToolbar); + mBtnAdd = (Button) findViewById(R.id.btn_add); + mBtnAdd.setOnClickListener(mBtnAddListener); + + int filterOption = AppBucketDrops.load(this); + loadResults(filterOption); + mEmptyView = findViewById(R.id.empty_drops); + mRecycler = (BucketRecyclerView) findViewById(R.id.rv_drops); + mRecycler.addItemDecoration(new Divider(this, LinearLayoutManager.VERTICAL)); + mRecycler.setItemAnimator(new DefaultItemAnimator()); + mRecycler.hideIfEmpty(mToolbar); + mRecycler.showIfEmpty(mEmptyView); + mAdapter = new AdapterDrops(this, mRealm, mResults, mAddListener, mMarkListener, mResetListener); + mAdapter.setHasStableIds(true); + mRecycler.setAdapter(mAdapter); + SimpleTouchCallback callback = new SimpleTouchCallback(mAdapter); + ItemTouchHelper helper = new ItemTouchHelper(callback); + helper.attachToRecyclerView(mRecycler); + initBackgroundImage(); + Util.scheduleAlarm(this); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + boolean handled = true; + int filterOption = Filter.NONE; + switch (id) { + case R.id.action_add: + showDialogAdd(); + break; + case R.id.action_sort_none: + filterOption = Filter.NONE; + break; + case R.id.action_sort_ascending_date: + filterOption = Filter.LEAST_TIME_LEFT; + break; + case R.id.action_sort_descending_date: + filterOption = Filter.MOST_TIME_LEFT; + break; + case R.id.action_show_complete: + filterOption = Filter.COMPLETE; + break; + case R.id.action_show_incomplete: + filterOption = Filter.INCOMPLETE; + break; + default: + handled = false; + break; + } + loadResults(filterOption); + AppBucketDrops.save(this, filterOption); + return handled; + } + + private void loadResults(int filterOption) { + switch (filterOption) { + case Filter.NONE: + mResults = mRealm.where(Drop.class).findAllAsync(); + break; + case Filter.LEAST_TIME_LEFT: + mResults = mRealm.where(Drop.class).findAllSortedAsync(FIELD_WHEN); + break; + case Filter.MOST_TIME_LEFT: + mResults = mRealm.where(Drop.class).findAllSortedAsync(FIELD_WHEN, Sort.DESCENDING); + break; + case Filter.COMPLETE: + mResults = mRealm.where(Drop.class).equalTo(FIELD_COMPLETED, true).findAllAsync(); + break; + case Filter.INCOMPLETE: + mResults = mRealm.where(Drop.class).equalTo(FIELD_COMPLETED, false).findAllAsync(); + break; + } + mResults.addChangeListener(mChangeListener); + } + + + @Override + protected void onStart() { + super.onStart(); + mResults.addChangeListener(mChangeListener); + } + + @Override + protected void onStop() { + super.onStop(); + mResults.removeChangeListener(mChangeListener); + } + + private void initBackgroundImage() { + ImageView background = (ImageView) findViewById(R.id.iv_background); + Glide.with(this) + .load(R.drawable.background) + .centerCrop() + .into(background); + } + +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/AppBucketDrops.java b/app/src/main/java/appcourse/vivz/bucketdrops/AppBucketDrops.java new file mode 100644 index 0000000..891d94d --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/AppBucketDrops.java @@ -0,0 +1,54 @@ +package appcourse.vivz.bucketdrops; + +import android.app.Application; +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Typeface; +import android.preference.PreferenceManager; +import android.widget.TextView; + +import io.realm.Realm; +import io.realm.RealmConfiguration; +import appcourse.vivz.bucketdrops.adapters.Filter; + +/** + * Created by vivz on 30/12/15. + * TODO change to custom fonts wherever appropriate + */ +public class AppBucketDrops extends Application { + + static String FILTER_OPTION = "filter"; + + @Override + public void onCreate() { + super.onCreate(); + RealmConfiguration configuration = new RealmConfiguration.Builder(this).build(); + Realm.setDefaultConfiguration(configuration); + } + + public static void save(Context context, int filterOption) { + + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); + SharedPreferences.Editor editor = pref.edit(); + editor.putInt(FILTER_OPTION, filterOption); + editor.apply(); + } + + public static int load(Context context) { + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); + int filterOption = pref.getInt(FILTER_OPTION, Filter.NONE); + return filterOption; + } + + public static void setRalewayRegular(Context context, TextView textView) { + Typeface typeface = Typeface.createFromAsset(context.getAssets(), "fonts/raleway_thin.ttf"); + textView.setTypeface(typeface); + } + + public static void setRalewayRegular(Context context, TextView... textViews) { + Typeface typeface = Typeface.createFromAsset(context.getAssets(), "fonts/raleway_thin.ttf"); + for (TextView textView : textViews) { + textView.setTypeface(typeface); + } + } +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/DialogAdd.java b/app/src/main/java/appcourse/vivz/bucketdrops/DialogAdd.java new file mode 100644 index 0000000..ac429db --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/DialogAdd.java @@ -0,0 +1,82 @@ +package appcourse.vivz.bucketdrops; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; + +import io.realm.Realm; +import appcourse.vivz.bucketdrops.beans.Drop; +import appcourse.vivz.bucketdrops.widgets.BucketPickerView; + +/** + * Created by vivz on 28/12/15. + * TODO make it full screen + custom fonts + * TODO add custom date picker + */ +public class DialogAdd extends DialogFragment { + + private ImageButton mBtnClose; + private EditText mInputWhat; + private BucketPickerView mInputWhen; + private Button mBtnAdd; + + private View.OnClickListener mBtnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + int id = v.getId(); + switch (id) { + case R.id.btn_add_it: + addAction(); + break; + } + dismiss(); + } + }; + + private void addAction() { + //get the value of the 'goal' or 'to-do' + //get the time when it was added + String what = mInputWhat.getText().toString(); + long now = System.currentTimeMillis(); + Realm realm = Realm.getDefaultInstance(); + Drop drop = new Drop(what, now, mInputWhen.getTime(), false); + realm.beginTransaction(); + realm.copyToRealm(drop); + realm.commitTransaction(); + realm.close(); + + } + + public DialogAdd() { + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogTheme); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.dialog_add, container, false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mBtnClose = (ImageButton) view.findViewById(R.id.btn_close); + mInputWhat = (EditText) view.findViewById(R.id.et_drop); + mInputWhen = (BucketPickerView) view.findViewById(R.id.bpv_date); + mBtnAdd = (Button) view.findViewById(R.id.btn_add_it); + AppBucketDrops.setRalewayRegular(getActivity(), mInputWhat, mBtnAdd); + mBtnClose.setOnClickListener(mBtnClickListener); + mBtnAdd.setOnClickListener(mBtnClickListener); + } +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/DialogMark.java b/app/src/main/java/appcourse/vivz/bucketdrops/DialogMark.java new file mode 100644 index 0000000..46d66dd --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/DialogMark.java @@ -0,0 +1,69 @@ +package appcourse.vivz.bucketdrops; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageButton; + +import appcourse.vivz.bucketdrops.adapters.CompleteListener; + +/** + * Created by vivz on 04/01/16. + * TODO custom fonts + */ +public class DialogMark extends DialogFragment { + + public static final String EXTRA_POSITION = "position"; + private ImageButton mBtnClose; + private Button mBtnCompleted; + private View.OnClickListener mBtnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.btn_completed: + markAsComplete(); + break; + } + dismiss(); + } + }; + private CompleteListener mListener; + + private void markAsComplete() { + Bundle arguments = getArguments(); + if (mListener != null && arguments != null) { + int position = arguments.getInt(EXTRA_POSITION); + mListener.onComplete(position); + } + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogTheme); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.dialog_mark, container, false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mBtnClose = (ImageButton) view.findViewById(R.id.btn_close); + mBtnCompleted = (Button) view.findViewById(R.id.btn_completed); + AppBucketDrops.setRalewayRegular(getActivity(), mBtnCompleted); + mBtnClose.setOnClickListener(mBtnClickListener); + mBtnCompleted.setOnClickListener(mBtnClickListener); + } + + public void setCompleteListener(CompleteListener mCompleteListener) { + mListener = mCompleteListener; + } +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/adapters/AdapterDrops.java b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/AdapterDrops.java new file mode 100644 index 0000000..3b49d77 --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/AdapterDrops.java @@ -0,0 +1,231 @@ +package appcourse.vivz.bucketdrops.adapters; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.RecyclerView; +import android.text.format.DateUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; + +import io.realm.Realm; +import io.realm.RealmResults; +import appcourse.vivz.bucketdrops.AppBucketDrops; +import appcourse.vivz.bucketdrops.R; +import appcourse.vivz.bucketdrops.beans.Drop; +import appcourse.vivz.bucketdrops.extras.Util; + +/** + * Created by vivz on 30/12/15. + */ +public class AdapterDrops extends RecyclerView.Adapter implements SwipeListener { + public static final int COUNT_FOOTER = 1; + public static final int COUNT_NO_ITEMS = 1; + public static final int ITEM = 0; + public static final int NO_ITEM = 1; + public static final int FOOTER = 2; + private final ResetListener mResetListener; + private MarkListener mMarkListener; + + private LayoutInflater mInflater; + private Realm mRealm; + private RealmResults mResults; + private AddListener mAddListener; + private int mFilterOption; + private Context mContext; + + + public AdapterDrops(Context context, Realm realm, RealmResults results, AddListener listener, MarkListener markListener, ResetListener resetListener) { + mContext = context; + mInflater = LayoutInflater.from(context); + update(results); + mRealm = realm; + mAddListener = listener; + mMarkListener = markListener; + mResetListener = resetListener; + } + + public void update(RealmResults results) { + mResults = results; + mFilterOption = AppBucketDrops.load(mContext); + notifyDataSetChanged(); + + } + + @Override + public long getItemId(int position) { + if (position < mResults.size()) { + return mResults.get(position).getAdded(); + } + return RecyclerView.NO_ID; + } + + @Override + public int getItemCount() { + if (!mResults.isEmpty()) { + return mResults.size() + COUNT_FOOTER; + } else { + if (mFilterOption == Filter.LEAST_TIME_LEFT + || mFilterOption == Filter.MOST_TIME_LEFT + || mFilterOption == Filter.NONE) { + return 0; + } else { + return COUNT_NO_ITEMS + COUNT_FOOTER; + } + } + } + + @Override + public int getItemViewType(int position) { + if (!mResults.isEmpty()) { + if (position < mResults.size()) { + return ITEM; + } else { + return FOOTER; + } + } else { + if (mFilterOption == Filter.COMPLETE || + mFilterOption == Filter.INCOMPLETE) { + if (position == 0) { + return NO_ITEM; + } else { + return FOOTER; + } + } else { + return ITEM; + } + } + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (viewType == FOOTER) { + View view = mInflater.inflate(R.layout.footer, parent, false); + return new FooterHolder(view, mAddListener); + } else if (viewType == NO_ITEM) { + View view = mInflater.inflate(R.layout.no_item, parent, false); + return new NoItemsHolder(view); + } else { + View view = mInflater.inflate(R.layout.row_drop, parent, false); + return new DropHolder(view, mMarkListener); + } + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { + if (holder instanceof DropHolder) { + DropHolder dropHolder = (DropHolder) holder; + Drop drop = mResults.get(position); + dropHolder.setWhat(drop.getWhat()); + dropHolder.setWhen(drop.getWhen()); + dropHolder.setBackground(drop.isCompleted()); + } + } + + @Override + public void onSwipe(int position) { + if (position < mResults.size()) { + mRealm.beginTransaction(); + mResults.get(position).removeFromRealm(); + mRealm.commitTransaction(); + notifyItemRemoved(position); + } + resetFilterIfEmpty(); + } + + private void resetFilterIfEmpty() { + if (mResults.isEmpty() && (mFilterOption == Filter.COMPLETE || + mFilterOption == Filter.INCOMPLETE)) { + mResetListener.onReset(); + } + } + + public void markComplete(int position) { + if (position < mResults.size()) { + mRealm.beginTransaction(); + mResults.get(position).setCompleted(true); + mRealm.commitTransaction(); + notifyItemChanged(position); + } + } + + public static class DropHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + + TextView mTextWhat; + TextView mTextWhen; + MarkListener mMarkListener; + Context mContext; + View mItemView; + + public DropHolder(View itemView, MarkListener listener) { + super(itemView); + mItemView = itemView; + mContext = itemView.getContext(); + itemView.setOnClickListener(this); + mTextWhat = (TextView) itemView.findViewById(R.id.tv_what); + mTextWhen = (TextView) itemView.findViewById(R.id.tv_when); + AppBucketDrops.setRalewayRegular(mContext, mTextWhat, mTextWhen); + mMarkListener = listener; + } + + public void setWhat(String what) { + mTextWhat.setText(what); + } + + @Override + public void onClick(View v) { + mMarkListener.onMark(getAdapterPosition()); + } + + public void setBackground(boolean completed) { + Drawable drawable; + if (completed) { + drawable = ContextCompat.getDrawable(mContext, R.color.bg_drop_complete); + } else { + drawable = ContextCompat.getDrawable(mContext, R.drawable.bg_row_drop); + } + Util.setBackground(mItemView, drawable); + } + + public void setWhen(long when) { + mTextWhen.setText(DateUtils.getRelativeTimeSpanString(when, System.currentTimeMillis(), DateUtils.DAY_IN_MILLIS, DateUtils.FORMAT_ABBREV_ALL)); + } + } + + public static class NoItemsHolder extends RecyclerView.ViewHolder { + + public NoItemsHolder(View itemView) { + super(itemView); + } + } + + public static class FooterHolder extends RecyclerView.ViewHolder implements View.OnClickListener { + + Context mContext; + Button mBtnAdd; + AddListener mListener; + + public FooterHolder(View itemView) { + super(itemView); + mContext = itemView.getContext(); + mBtnAdd = (Button) itemView.findViewById(R.id.btn_footer); + AppBucketDrops.setRalewayRegular(mContext, mBtnAdd); + mBtnAdd.setOnClickListener(this); + } + + public FooterHolder(View itemView, AddListener listener) { + super(itemView); + mBtnAdd = (Button) itemView.findViewById(R.id.btn_footer); + mBtnAdd.setOnClickListener(this); + mListener = listener; + } + + @Override + public void onClick(View v) { + mListener.add(); + } + } +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/adapters/AddListener.java b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/AddListener.java new file mode 100644 index 0000000..c6b6149 --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/AddListener.java @@ -0,0 +1,8 @@ +package appcourse.vivz.bucketdrops.adapters; + +/** + * Created by vivz on 03/01/16. + */ +public interface AddListener { + void add(); +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/adapters/CompleteListener.java b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/CompleteListener.java new file mode 100644 index 0000000..880c7bc --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/CompleteListener.java @@ -0,0 +1,8 @@ +package appcourse.vivz.bucketdrops.adapters; + +/** + * Created by vivz on 04/01/16. + */ +public interface CompleteListener { + public void onComplete(int position); +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/adapters/Divider.java b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/Divider.java new file mode 100644 index 0000000..a6443ea --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/Divider.java @@ -0,0 +1,60 @@ +package appcourse.vivz.bucketdrops.adapters; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v4.content.ContextCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import appcourse.vivz.bucketdrops.R; + +/** + * Created by vivz on 03/01/16. + */ +public class Divider extends RecyclerView.ItemDecoration { + + private Drawable mDivider; + private int mOrientation; + + public Divider(Context context, int orientation) { + mDivider = ContextCompat.getDrawable(context, R.drawable.divider); + if (orientation != LinearLayoutManager.VERTICAL) { + throw new IllegalArgumentException("This Item Decoration can be used only with a RecyclerView that uses a LinearLayoutManager with vertical orientation"); + } + mOrientation = orientation; + } + + @Override + public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { + if (mOrientation == LinearLayoutManager.VERTICAL) { + drawHorizontalDivider(c, parent, state); + } + } + + private void drawHorizontalDivider(Canvas c, RecyclerView parent, RecyclerView.State state) { + int left, top, right, bottom; + left = parent.getPaddingLeft(); + right = parent.getWidth() - parent.getPaddingRight(); + int count = parent.getChildCount(); + for (int i = 0; i < count; i++) { + if (AdapterDrops.FOOTER != parent.getAdapter().getItemViewType(i)) { + View current = parent.getChildAt(i); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) current.getLayoutParams(); + top = current.getTop() - params.topMargin; + bottom = top + mDivider.getIntrinsicHeight(); + mDivider.setBounds(left, top, right, bottom); + mDivider.draw(c); + } + } + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + if (mOrientation == LinearLayoutManager.VERTICAL) { + outRect.set(0, 0, 0, mDivider.getIntrinsicHeight()); + } + } +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/adapters/Filter.java b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/Filter.java new file mode 100644 index 0000000..7356c37 --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/Filter.java @@ -0,0 +1,12 @@ +package appcourse.vivz.bucketdrops.adapters; + +/** + * Created by vivz on 05/01/16. + */ +public interface Filter { + int NONE = 0; + int MOST_TIME_LEFT = 1; + int LEAST_TIME_LEFT = 2; + int COMPLETE = 3; + int INCOMPLETE = 4; +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/adapters/MarkListener.java b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/MarkListener.java new file mode 100644 index 0000000..63cd536 --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/MarkListener.java @@ -0,0 +1,8 @@ +package appcourse.vivz.bucketdrops.adapters; + +/** + * Created by vivz on 04/01/16. + */ +public interface MarkListener { + void onMark(int position); +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/adapters/ResetListener.java b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/ResetListener.java new file mode 100644 index 0000000..50b57aa --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/ResetListener.java @@ -0,0 +1,8 @@ +package appcourse.vivz.bucketdrops.adapters; + +/** + * Created by vivz on 06/01/16. + */ +public interface ResetListener { + void onReset(); +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/adapters/SimpleTouchCallback.java b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/SimpleTouchCallback.java new file mode 100644 index 0000000..5026f0a --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/SimpleTouchCallback.java @@ -0,0 +1,57 @@ +package appcourse.vivz.bucketdrops.adapters; + +import android.graphics.Canvas; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; + +/** + * Created by vivz on 04/01/16. + */ +public class SimpleTouchCallback extends ItemTouchHelper.Callback { + private SwipeListener mListener; + + public SimpleTouchCallback(SwipeListener listener) { + mListener = listener; + } + + @Override + public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { + return makeMovementFlags(0, ItemTouchHelper.END); + } + + @Override + public boolean isLongPressDragEnabled() { + return false; + } + + @Override + public boolean isItemViewSwipeEnabled() { + return true; + } + + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + return false; + } + + @Override + public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { + if (viewHolder instanceof AdapterDrops.DropHolder) { + super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + } + } + + @Override + public void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) { + if (viewHolder instanceof AdapterDrops.DropHolder) { + super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive); + } + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { + if (viewHolder instanceof AdapterDrops.DropHolder) { + mListener.onSwipe(viewHolder.getLayoutPosition()); + } + } +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/adapters/SwipeListener.java b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/SwipeListener.java new file mode 100644 index 0000000..403e4eb --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/adapters/SwipeListener.java @@ -0,0 +1,8 @@ +package appcourse.vivz.bucketdrops.adapters; + +/** + * Created by vivz on 04/01/16. + */ +public interface SwipeListener { + void onSwipe(int position); +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/beans/Drop.java b/app/src/main/java/appcourse/vivz/bucketdrops/beans/Drop.java new file mode 100644 index 0000000..9516505 --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/beans/Drop.java @@ -0,0 +1,57 @@ +package appcourse.vivz.bucketdrops.beans; + +import io.realm.RealmObject; +import io.realm.annotations.PrimaryKey; + +/** + * Created by vivz on 30/12/15. + */ +public class Drop extends RealmObject { + public Drop(String what, long added, long when, boolean completed) { + this.what = what; + this.added = added; + this.when = when; + this.completed = completed; + } + + private String what; + @PrimaryKey + private long added; + private long when; + private boolean completed; + + public Drop() { + } + + public String getWhat() { + return what; + } + + public void setWhat(String what) { + this.what = what; + } + + public long getAdded() { + return added; + } + + public void setAdded(long added) { + this.added = added; + } + + public long getWhen() { + return when; + } + + public void setWhen(long when) { + this.when = when; + } + + public boolean isCompleted() { + return completed; + } + + public void setCompleted(boolean completed) { + this.completed = completed; + } +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/extras/Util.java b/app/src/main/java/appcourse/vivz/bucketdrops/extras/Util.java new file mode 100644 index 0000000..bb446c9 --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/extras/Util.java @@ -0,0 +1,52 @@ +package appcourse.vivz.bucketdrops.extras; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.view.View; + +import java.util.List; + +import appcourse.vivz.bucketdrops.services.NotificationService; + +/** + * Created by vivz on 31/12/15. + */ +public class Util { + public static final int DELAY = 1000; + public static final int FREQUENCY = 1000 * 3600 * 4; + + public static void showViews(List views) { + for (View view : views) { + view.setVisibility(View.VISIBLE); + } + } + + public static void hideViews(List views) { + for (View view : views) { + view.setVisibility(View.GONE); + } + } + + public static boolean moreThanJellyBean() { + return Build.VERSION.SDK_INT > 15; + } + + public static void setBackground(View view, Drawable drawable) { + if (moreThanJellyBean()) { + view.setBackground(drawable); + } else { + view.setBackgroundDrawable(drawable); + } + } + + public static void scheduleAlarm(Context context) { + AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + Intent intent = new Intent(context, NotificationService.class); + PendingIntent pendingIntent = PendingIntent.getService(context, 100, intent, PendingIntent.FLAG_UPDATE_CURRENT); + manager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, DELAY, FREQUENCY, pendingIntent); + } +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/receivers/BootReceiver.java b/app/src/main/java/appcourse/vivz/bucketdrops/receivers/BootReceiver.java new file mode 100644 index 0000000..194cf0d --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/receivers/BootReceiver.java @@ -0,0 +1,17 @@ +package appcourse.vivz.bucketdrops.receivers; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import appcourse.vivz.bucketdrops.extras.Util; + +public class BootReceiver extends BroadcastReceiver { + public BootReceiver() { + } + + @Override + public void onReceive(Context context, Intent intent) { + Util.scheduleAlarm(context); + } +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/services/NotificationService.java b/app/src/main/java/appcourse/vivz/bucketdrops/services/NotificationService.java new file mode 100644 index 0000000..3a7a3df --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/services/NotificationService.java @@ -0,0 +1,72 @@ +package appcourse.vivz.bucketdrops.services; + +import android.app.IntentService; +import android.app.Notification; +import android.content.Intent; + +import br.com.goncalves.pugnotification.notification.PugNotification; +import io.realm.Realm; +import io.realm.RealmResults; +import appcourse.vivz.bucketdrops.ActivityMain; +import appcourse.vivz.bucketdrops.R; +import appcourse.vivz.bucketdrops.beans.Drop; + + +/** + * An {@link IntentService} subclass for handling asynchronous task requests in + * a service on a separate handler thread. + *

+ * TODO: Customize class - update intent actions and extra parameters. + */ +public class NotificationService extends IntentService { + + public NotificationService() { + super("NotificationService"); + } + + @Override + protected void onHandleIntent(Intent intent) { + Realm realm = null; + try { + realm = Realm.getDefaultInstance(); + RealmResults results = realm.where(Drop.class).equalTo("completed", false).findAll(); + for (Drop current : results) { + if (isNotificationNeeded(current.getAdded(), current.getWhen())) { + fireNotification(current); + } + } + + } finally { + if (realm != null) { + realm.close(); + } + } + } + + private void fireNotification(Drop drop) { + String message = getString(R.string.notif_message) + "\"" + drop.getWhat() + "\""; + PugNotification.with(this) + .load() + .title(R.string.notif_title) + .message(message) + .bigTextStyle(R.string.notif_long_message) + .smallIcon(R.drawable.ic_drop) + .largeIcon(R.drawable.ic_drop) + .flags(Notification.DEFAULT_ALL) + .autoCancel(true) + .click(ActivityMain.class) + .simple() + .build(); + } + + private boolean isNotificationNeeded(long added, long when) { + long now = System.currentTimeMillis(); + if (now > when) { + return false; + } else { + long difference90 = (long) (0.9 * (when - added)); + return (now > (added + difference90)) ? true : false; + } + } + +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/widgets/BucketPickerView.java b/app/src/main/java/appcourse/vivz/bucketdrops/widgets/BucketPickerView.java new file mode 100644 index 0000000..72fe267 --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/widgets/BucketPickerView.java @@ -0,0 +1,288 @@ +package appcourse.vivz.bucketdrops.widgets; + +import android.content.Context; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Parcelable; +import android.support.v4.content.ContextCompat; +import android.util.AttributeSet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.text.SimpleDateFormat; +import java.util.Calendar; + +import appcourse.vivz.bucketdrops.AppBucketDrops; +import appcourse.vivz.bucketdrops.R; + +/** + * Created by vivz on 10/01/16. + */ +public class BucketPickerView extends LinearLayout implements View.OnTouchListener { + public static final int LEFT = 0; + public static final int TOP = 1; + public static final int RIGHT = 2; + public static final int BOTTOM = 3; + public static final int DELAY = 250; + private Calendar mCalendar; + private TextView mTextDate; + private TextView mTextMonth; + private TextView mTextYear; + private SimpleDateFormat mFormatter; + private String TAG = "VIVZ"; + private boolean mIncrement; + private boolean mDecrement; + + private Drawable mUpNormal; + private Drawable mUpPressed; + private Drawable mDownNormal; + private Drawable mDownPressed; + + private Handler mHandler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + if (mIncrement) { + increment(mActiveId); + } + if (mDecrement) { + decrement(mActiveId); + } + if (mIncrement || mDecrement) { + mHandler.sendEmptyMessageDelayed(MESSAGE_WHAT, DELAY); + } + return true; + } + }); + private int MESSAGE_WHAT = 123; + private int mActiveId; + + public BucketPickerView(Context context) { + super(context); + init(context); + } + + public BucketPickerView(Context context, AttributeSet attrs) { + super(context, attrs); + init(context); + } + + public BucketPickerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context); + } + + private void init(Context context) { + View view = LayoutInflater.from(context).inflate(R.layout.bucket_picker_view, this); + mCalendar = Calendar.getInstance(); + mFormatter = new SimpleDateFormat("MMM"); + mUpNormal = ContextCompat.getDrawable(context, R.drawable.up_normal); + mUpPressed = ContextCompat.getDrawable(context, R.drawable.up_pressed); + mDownNormal = ContextCompat.getDrawable(context, R.drawable.down_normal); + mDownPressed = ContextCompat.getDrawable(context, R.drawable.down_pressed); + } + + @Override + protected Parcelable onSaveInstanceState() { + Log.d(TAG, "onSaveInstanceState: "); + Bundle bundle = new Bundle(); + bundle.putParcelable("super", super.onSaveInstanceState()); + bundle.putInt("date", mCalendar.get(Calendar.DATE)); + bundle.putInt("month", mCalendar.get(Calendar.MONTH)); + bundle.putInt("year", mCalendar.get(Calendar.YEAR)); + return bundle; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + Log.d(TAG, "onRestoreInstanceState: "); + if (state instanceof Parcelable) { + Bundle bundle = (Bundle) state; + state = bundle.getParcelable("super"); + int date = bundle.getInt("date"); + int month = bundle.getInt("month"); + int year = bundle.getInt("year"); + update(date, month, year, 0, 0, 0); + } + super.onRestoreInstanceState(state); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mTextDate = (TextView) this.findViewById(R.id.tv_date); + mTextMonth = (TextView) this.findViewById(R.id.tv_month); + mTextYear = (TextView) this.findViewById(R.id.tv_year); + AppBucketDrops.setRalewayRegular(getContext(), mTextDate, mTextMonth, mTextYear); + mTextDate.setOnTouchListener(this); + mTextMonth.setOnTouchListener(this); + mTextYear.setOnTouchListener(this); + int date = mCalendar.get(Calendar.DATE); + int month = mCalendar.get(Calendar.MONTH); + int year = mCalendar.get(Calendar.YEAR); + update(date, month, year, 0, 0, 0); + } + + private void update(int date, int month, int year, int hour, int minute, int second) { + mCalendar.set(Calendar.DATE, date); + mCalendar.set(Calendar.MONTH, month); + mCalendar.set(Calendar.YEAR, year); + mCalendar.set(Calendar.HOUR, hour); + mCalendar.set(Calendar.MINUTE, minute); + mCalendar.set(Calendar.SECOND, second); + mTextYear.setText(year + ""); + mTextDate.setText(date + ""); + mTextMonth.setText(mFormatter.format(mCalendar.getTime())); + } + + public long getTime() { + return mCalendar.getTimeInMillis(); + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (v.getId()) { + case R.id.tv_date: + processEventsFor(mTextDate, event); + break; + case R.id.tv_month: + processEventsFor(mTextMonth, event); + break; + case R.id.tv_year: + processEventsFor(mTextYear, event); + break; + } + return true; + } + + private void processEventsFor(TextView textView, MotionEvent event) { + Drawable[] drawables = textView.getCompoundDrawables(); + if (hasDrawableTop(drawables) && hasDrawableBottom(drawables)) { + Rect topBounds = drawables[TOP].getBounds(); + Rect bottomBounds = drawables[BOTTOM].getBounds(); + float x = event.getX(); + float y = event.getY(); + mActiveId = textView.getId(); + if (topDrawableHit(textView, topBounds.height(), x, y)) { + if (isActionDown(event)) { + mIncrement = true; + increment(textView.getId()); + mHandler.removeMessages(MESSAGE_WHAT); + mHandler.sendEmptyMessageDelayed(MESSAGE_WHAT, DELAY); + toggleDrawable(textView, true); + } + if (isActionUpOrCancel(event)) { + mIncrement = false; + toggleDrawable(textView, false); + } + + } else if (bottomDrawableHit(textView, bottomBounds.height(), x, y)) { + if (isActionDown(event)) { + mDecrement = true; + decrement(textView.getId()); + mHandler.removeMessages(MESSAGE_WHAT); + mHandler.sendEmptyMessageDelayed(MESSAGE_WHAT, DELAY); + toggleDrawable(textView, true); + } + if (isActionUpOrCancel(event)) { + mDecrement = false; + toggleDrawable(textView, false); + } + } else { + mIncrement = false; + mDecrement = false; + toggleDrawable(textView, false); + } + } + } + + private void increment(int id) { + switch (id) { + case R.id.tv_date: + mCalendar.add(Calendar.DATE, 1); + break; + case R.id.tv_month: + mCalendar.add(Calendar.MONTH, 1); + break; + case R.id.tv_year: + mCalendar.add(Calendar.YEAR, 1); + break; + } + set(mCalendar); + } + + private void set(Calendar calendar) { + int date = calendar.get(Calendar.DATE); + int year = calendar.get(Calendar.YEAR); + mTextDate.setText(date + ""); + mTextYear.setText(year + ""); + mTextMonth.setText(mFormatter.format(mCalendar.getTime())); + } + + private void decrement(int id) { + switch (id) { + case R.id.tv_date: + mCalendar.add(Calendar.DATE, -1); + break; + case R.id.tv_month: + mCalendar.add(Calendar.MONTH, -1); + break; + case R.id.tv_year: + mCalendar.add(Calendar.YEAR, -1); + break; + } + set(mCalendar); + } + + private boolean isActionDown(MotionEvent event) { + return event.getAction() == MotionEvent.ACTION_DOWN; + } + + private boolean isActionUpOrCancel(MotionEvent event) { + return event.getAction() == MotionEvent.ACTION_UP + || event.getAction() == MotionEvent.ACTION_CANCEL; + } + + private boolean topDrawableHit(TextView textView, int drawableHeight, float x, float y) { + int xmin = textView.getPaddingLeft(); + int xmax = textView.getWidth() - textView.getPaddingRight(); + int ymin = textView.getPaddingTop(); + int ymax = textView.getPaddingTop() + drawableHeight; + return x > xmin && x < xmax && y > ymin && y < ymax; + } + + private boolean bottomDrawableHit(TextView textView, int drawableHeight, float x, float y) { + int xmin = textView.getPaddingLeft(); + int xmax = textView.getWidth() - textView.getPaddingRight(); + int ymax = textView.getHeight() - textView.getPaddingBottom(); + int ymin = ymax - drawableHeight; + return x > xmin && x < xmax && y > ymin && y < ymax; + } + + private boolean hasDrawableTop(Drawable[] drawables) { + return drawables[TOP] != null; + } + + private boolean hasDrawableBottom(Drawable[] drawables) { + return drawables[BOTTOM] != null; + } + + private void toggleDrawable(TextView textView, boolean pressed) { + if (pressed) { + if (mIncrement) { + textView.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.up_pressed, 0, R.drawable.down_normal); + } + if (mDecrement) { + textView.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.up_normal, 0, R.drawable.down_pressed); + } + } else { + textView.setCompoundDrawablesWithIntrinsicBounds(0, R.drawable.up_normal, 0, R.drawable.down_normal); + } + } +} diff --git a/app/src/main/java/appcourse/vivz/bucketdrops/widgets/BucketRecyclerView.java b/app/src/main/java/appcourse/vivz/bucketdrops/widgets/BucketRecyclerView.java new file mode 100644 index 0000000..602d6e8 --- /dev/null +++ b/app/src/main/java/appcourse/vivz/bucketdrops/widgets/BucketRecyclerView.java @@ -0,0 +1,106 @@ +package appcourse.vivz.bucketdrops.widgets; + +import android.content.Context; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.View; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import appcourse.vivz.bucketdrops.extras.Util; + +/** + * Created by vivz on 31/12/15. + */ +public class BucketRecyclerView extends RecyclerView { + + private List mNonEmptyViews = Collections.emptyList(); + private List mEmptyViews = Collections.emptyList(); + private AdapterDataObserver mObserver = new AdapterDataObserver() { + @Override + public void onChanged() { + toggleViews(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount) { + toggleViews(); + } + + @Override + public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { + toggleViews(); + } + + @Override + public void onItemRangeInserted(int positionStart, int itemCount) { + toggleViews(); + } + + @Override + public void onItemRangeRemoved(int positionStart, int itemCount) { + toggleViews(); + } + + @Override + public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { + toggleViews(); + } + }; + + private void toggleViews() { + if (getAdapter() != null && !mEmptyViews.isEmpty() && !mNonEmptyViews.isEmpty()) { + if (getAdapter().getItemCount() == 0) { + + //show all the empty views + Util.showViews(mEmptyViews); + //hide the RecyclerView + setVisibility(View.GONE); + + //hide all the views which are meant to be hidden + Util.hideViews(mNonEmptyViews); + } else { + //hide all the empty views + Util.showViews(mNonEmptyViews); + + //show the RecyclerView + setVisibility(View.VISIBLE); + + //hide all the views which are meant to be hidden + Util.hideViews(mEmptyViews); + } + } + } + + public BucketRecyclerView(Context context) { + super(context); + } + + public BucketRecyclerView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public BucketRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public void setAdapter(Adapter adapter) { + super.setAdapter(adapter); + if (adapter != null) { + adapter.registerAdapterDataObserver(mObserver); + } + mObserver.onChanged(); + } + + public void hideIfEmpty(View... views) { + mNonEmptyViews = Arrays.asList(views); + } + + public void showIfEmpty(View... emptyViews) { + mEmptyViews = Arrays.asList(emptyViews); + } +} diff --git a/app/src/main/java/slidenerd/vivz/bucketdrops/ActivityMain.java b/app/src/main/java/slidenerd/vivz/bucketdrops/ActivityMain.java deleted file mode 100644 index 8b16200..0000000 --- a/app/src/main/java/slidenerd/vivz/bucketdrops/ActivityMain.java +++ /dev/null @@ -1,44 +0,0 @@ -package slidenerd.vivz.bucketdrops; - -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.view.View; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.Toast; - -import com.bumptech.glide.Glide; - -public class ActivityMain extends AppCompatActivity { - - Toolbar mToolbar; - Button mBtnAdd; - private View.OnClickListener mBtnAddListener = new View.OnClickListener() { - - @Override - public void onClick(View v) { - Toast.makeText(ActivityMain.this, "Button was clicked from anonymous listener pointed to by a variable", Toast.LENGTH_SHORT).show(); - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - mToolbar = (Toolbar) findViewById(R.id.toolbar); - mBtnAdd = (Button) findViewById(R.id.btn_add); - - mBtnAdd.setOnClickListener(mBtnAddListener); - setSupportActionBar(mToolbar); - initBackgroundImage(); - } - - private void initBackgroundImage() { - ImageView background = (ImageView) findViewById(R.id.iv_background); - Glide.with(this) - .load(R.drawable.background) - .centerCrop() - .into(background); - } -} diff --git a/app/src/main/res/drawable-hdpi/ic_action_add.png b/app/src/main/res/drawable-hdpi/ic_action_add.png new file mode 100644 index 0000000..7800ba3 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_add.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_check.png b/app/src/main/res/drawable-hdpi/ic_check.png new file mode 100755 index 0000000..8a6a2dc Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_check.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_down_normal.png b/app/src/main/res/drawable-hdpi/ic_down_normal.png new file mode 100755 index 0000000..d0f0563 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_down_normal.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_down_pressed.png b/app/src/main/res/drawable-hdpi/ic_down_pressed.png new file mode 100755 index 0000000..a24dc3d Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_down_pressed.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_drop.png b/app/src/main/res/drawable-hdpi/ic_drop.png new file mode 100755 index 0000000..368228b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_drop.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_up_normal.png b/app/src/main/res/drawable-hdpi/ic_up_normal.png new file mode 100755 index 0000000..b95a444 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_up_normal.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_up_pressed.png b/app/src/main/res/drawable-hdpi/ic_up_pressed.png new file mode 100755 index 0000000..5c7512e Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_up_pressed.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_add.png b/app/src/main/res/drawable-mdpi/ic_action_add.png new file mode 100644 index 0000000..ed40b2b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_add.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_check.png b/app/src/main/res/drawable-mdpi/ic_check.png new file mode 100755 index 0000000..b7bba0f Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_check.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_down_normal.png b/app/src/main/res/drawable-mdpi/ic_down_normal.png new file mode 100755 index 0000000..15837d2 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_down_normal.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_down_pressed.png b/app/src/main/res/drawable-mdpi/ic_down_pressed.png new file mode 100755 index 0000000..795228b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_down_pressed.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_drop.png b/app/src/main/res/drawable-mdpi/ic_drop.png new file mode 100755 index 0000000..2360b9a Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_drop.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_up_normal.png b/app/src/main/res/drawable-mdpi/ic_up_normal.png new file mode 100755 index 0000000..0a7db2a Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_up_normal.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_up_pressed.png b/app/src/main/res/drawable-mdpi/ic_up_pressed.png new file mode 100755 index 0000000..5f7806f Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_up_pressed.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_add.png b/app/src/main/res/drawable-xhdpi/ic_action_add.png new file mode 100644 index 0000000..185be8a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_add.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_check.png b/app/src/main/res/drawable-xhdpi/ic_check.png new file mode 100755 index 0000000..dda1727 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_check.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_down_normal.png b/app/src/main/res/drawable-xhdpi/ic_down_normal.png new file mode 100755 index 0000000..872a298 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_down_normal.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_down_pressed.png b/app/src/main/res/drawable-xhdpi/ic_down_pressed.png new file mode 100755 index 0000000..884f591 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_down_pressed.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_drop.png b/app/src/main/res/drawable-xhdpi/ic_drop.png new file mode 100755 index 0000000..6a82189 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_drop.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_up_normal.png b/app/src/main/res/drawable-xhdpi/ic_up_normal.png new file mode 100755 index 0000000..5e2caa6 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_up_normal.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_up_pressed.png b/app/src/main/res/drawable-xhdpi/ic_up_pressed.png new file mode 100755 index 0000000..be7783a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_up_pressed.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_add.png b/app/src/main/res/drawable-xxhdpi/ic_action_add.png new file mode 100644 index 0000000..2ab780a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_add.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_check.png b/app/src/main/res/drawable-xxhdpi/ic_check.png new file mode 100755 index 0000000..1ef4645 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_check.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_down_normal.png b/app/src/main/res/drawable-xxhdpi/ic_down_normal.png new file mode 100755 index 0000000..f01e619 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_down_normal.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_down_pressed.png b/app/src/main/res/drawable-xxhdpi/ic_down_pressed.png new file mode 100755 index 0000000..0de927c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_down_pressed.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_drop.png b/app/src/main/res/drawable-xxhdpi/ic_drop.png new file mode 100755 index 0000000..407e836 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_drop.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_up_normal.png b/app/src/main/res/drawable-xxhdpi/ic_up_normal.png new file mode 100755 index 0000000..b6d5951 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_up_normal.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_up_pressed.png b/app/src/main/res/drawable-xxhdpi/ic_up_pressed.png new file mode 100755 index 0000000..d604e96 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_up_pressed.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_add.png b/app/src/main/res/drawable-xxxhdpi/ic_action_add.png new file mode 100644 index 0000000..e6ca376 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_add.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_check.png b/app/src/main/res/drawable-xxxhdpi/ic_check.png new file mode 100755 index 0000000..2dbe64a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_check.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_drop.png b/app/src/main/res/drawable-xxxhdpi/ic_drop.png new file mode 100755 index 0000000..22f60ee Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_drop.png differ diff --git a/app/src/main/res/drawable/bg_et_drop.xml b/app/src/main/res/drawable/bg_et_drop.xml new file mode 100644 index 0000000..69355e2 --- /dev/null +++ b/app/src/main/res/drawable/bg_et_drop.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_picker_item_background.xml b/app/src/main/res/drawable/bg_picker_item_background.xml new file mode 100644 index 0000000..879310f --- /dev/null +++ b/app/src/main/res/drawable/bg_picker_item_background.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_row_drop.xml b/app/src/main/res/drawable/bg_row_drop.xml new file mode 100644 index 0000000..bdc641e --- /dev/null +++ b/app/src/main/res/drawable/bg_row_drop.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/divider.xml b/app/src/main/res/drawable/divider.xml new file mode 100644 index 0000000..3287fa1 --- /dev/null +++ b/app/src/main/res/drawable/divider.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/down_normal.xml b/app/src/main/res/drawable/down_normal.xml new file mode 100644 index 0000000..fa10ac9 --- /dev/null +++ b/app/src/main/res/drawable/down_normal.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/down_pressed.xml b/app/src/main/res/drawable/down_pressed.xml new file mode 100644 index 0000000..7a88563 --- /dev/null +++ b/app/src/main/res/drawable/down_pressed.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/up_normal.xml b/app/src/main/res/drawable/up_normal.xml new file mode 100644 index 0000000..692ec61 --- /dev/null +++ b/app/src/main/res/drawable/up_normal.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/up_pressed.xml b/app/src/main/res/drawable/up_pressed.xml new file mode 100644 index 0000000..38eb67b --- /dev/null +++ b/app/src/main/res/drawable/up_pressed.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e582ca3..8e5cea8 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,20 +1,29 @@ + tools:context="appcourse.vivz.bucketdrops.ActivityMain"> + + - - + android:layout_below="@id/toolbar" + app:layoutManager="android.support.v7.widget.LinearLayoutManager" /> + + - diff --git a/app/src/main/res/layout/bucket_picker_view.xml b/app/src/main/res/layout/bucket_picker_view.xml new file mode 100644 index 0000000..0c759ac --- /dev/null +++ b/app/src/main/res/layout/bucket_picker_view.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_add.xml b/app/src/main/res/layout/dialog_add.xml index 6a1326c..5af35ee 100644 --- a/app/src/main/res/layout/dialog_add.xml +++ b/app/src/main/res/layout/dialog_add.xml @@ -1,46 +1,54 @@ - + - + - + - + - + -