diff --git a/app/build.gradle b/app/build.gradle index 8abaf2d..a160e36 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,7 +8,7 @@ android { minSdkVersion 16 targetSdkVersion 27 versionCode 2 - versionName "2.6.5" + versionName "2.6.6" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/main/java/com/arvind/quote/MainActivity.java b/app/src/main/java/com/arvind/quote/MainActivity.java index 83f7dc6..d517382 100644 --- a/app/src/main/java/com/arvind/quote/MainActivity.java +++ b/app/src/main/java/com/arvind/quote/MainActivity.java @@ -58,7 +58,7 @@ public class MainActivity extends AppCompatActivity implements PreferenceFragmen // Application's Shared Preferences private SharedPreferences sharedPreferences; // Theme ID - from styles.xml - private int themeId = R.style.AppTheme; + public static int themeId = R.style.AppTheme; // Keep track of previous selected Drawer Item // to make sure the same fragment isn't instantiated again private MenuItem previousItem; diff --git a/app/src/main/java/com/arvind/quote/adapter/FavQuoteAdapter.java b/app/src/main/java/com/arvind/quote/adapter/FavQuoteAdapter.java index d041025..3471260 100644 --- a/app/src/main/java/com/arvind/quote/adapter/FavQuoteAdapter.java +++ b/app/src/main/java/com/arvind/quote/adapter/FavQuoteAdapter.java @@ -13,6 +13,7 @@ import com.arvind.quote.R; import com.arvind.quote.database.FavDatabaseHelper; +import com.arvind.quote.fragment.FavQuoteFragment; import com.arvind.quote.utils.CommonUtils; import java.util.List; @@ -57,7 +58,7 @@ public int getItemCount() { return favQuoteList.size(); } - public class QuoteViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener { + public class QuoteViewHolder extends RecyclerView.ViewHolder { final TextView quoteTextView; final TextView authorTextView; @@ -68,24 +69,35 @@ public class QuoteViewHolder extends RecyclerView.ViewHolder implements View.OnL quoteTextView = itemView.findViewById(R.id.quote_text_view); authorTextView = itemView.findViewById(R.id.author_text_view); - itemView.setOnLongClickListener(this); + itemView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + CommonUtils.shareQuote(context, favQuoteList.get(getAdapterPosition())); + return true; + } + }); + // Implement Simple DoubleTap listener final GestureDetector gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { + final Quote quote = favQuoteList.get(getAdapterPosition()); new AlertDialog .Builder(context) .setIcon(android.R.drawable.ic_menu_delete) - .setTitle("Confirm Deletion of Quote ?") + .setTitle("Confirm Deletion of Quote") .setMessage("Doing this will remove this quote from Favorites list") .setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { FavDatabaseHelper favDatabaseHelper = FavDatabaseHelper.getInstance(context); - favDatabaseHelper.removeFavQuote(favQuoteList.get(getAdapterPosition()).getId()); - favQuoteList.remove(favQuoteList.get(getAdapterPosition())); + favDatabaseHelper.removeFavQuote(quote.getId()); + quote.setStarred(false); + favQuoteList.remove(quote); notifyItemRemoved(getAdapterPosition()); + if(getItemCount() == 0) + FavQuoteFragment.showDefaultFragLayout(); } }) .setNegativeButton("No", new DialogInterface.OnClickListener() { @@ -110,11 +122,5 @@ public boolean onTouch(View view, MotionEvent motionEvent) { } }); } - - @Override - public boolean onLongClick(View v) { - CommonUtils.shareQuote(context, favQuoteList.get(getAdapterPosition())); - return true; - } } } \ No newline at end of file diff --git a/app/src/main/java/com/arvind/quote/adapter/QuoteAdapter.java b/app/src/main/java/com/arvind/quote/adapter/QuoteAdapter.java index a7b5885..7868fcd 100644 --- a/app/src/main/java/com/arvind/quote/adapter/QuoteAdapter.java +++ b/app/src/main/java/com/arvind/quote/adapter/QuoteAdapter.java @@ -2,6 +2,7 @@ import android.content.Context; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -17,6 +18,7 @@ public class QuoteAdapter extends RecyclerView.Adapter quoteList; private Context context = null; + public static boolean isClickable = true; public QuoteAdapter(Context context, List quoteDetails) { this.context = context; @@ -88,6 +90,8 @@ public class QuoteViewHolder extends RecyclerView.ViewHolder { starQuoteView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + if(!isClickable) + return; Quote selectedQuote = quoteList.get(getAdapterPosition()); if (selectedQuote.isStarred()) { CommonUtils.removeFromFavQuotesList(context, selectedQuote.getId()); @@ -104,6 +108,9 @@ public void onClick(View v) { itemView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View view) { + Log.d("QAdapter", "kek"); + if(!isClickable) + return false; CommonUtils.shareQuote(context, quoteList.get(getAdapterPosition())); return true; } diff --git a/app/src/main/java/com/arvind/quote/fragment/FavQuoteFragment.java b/app/src/main/java/com/arvind/quote/fragment/FavQuoteFragment.java index f8b1d92..274187d 100644 --- a/app/src/main/java/com/arvind/quote/fragment/FavQuoteFragment.java +++ b/app/src/main/java/com/arvind/quote/fragment/FavQuoteFragment.java @@ -1,5 +1,6 @@ package com.arvind.quote.fragment; +import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.Nullable; @@ -13,6 +14,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.LinearLayout; import com.arvind.quote.MainActivity; import com.arvind.quote.R; @@ -27,41 +29,45 @@ public class FavQuoteFragment extends Fragment { private final String TAG = "FavQuoteFragment"; private SharedPreferences sharedPreferences; private Snackbar snackbar; + private static LinearLayout favQuoteDefaultLayout; + private static RecyclerView quoteRecyclerView; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + // RecyclerView's Adapter - Detects change on DataSet + FavDatabaseHelper dbHalp = FavDatabaseHelper.getInstance(getActivity().getApplicationContext()); + View view = inflater.inflate(R.layout.fav_quote_fragment, container, false); MainActivity.setActionBarTitle("FavQuotes"); // RecyclerView Object - RecyclerView quoteRecyclerView = view.findViewById(R.id.fav_quote_recyclerview); + quoteRecyclerView = view.findViewById(R.id.fav_quote_recyclerview); - // RecyclerView's Adapter - Detects change on DataSet - FavDatabaseHelper dbHalp = FavDatabaseHelper.getInstance(getActivity().getApplicationContext()); + favQuoteDefaultLayout = view.findViewById(R.id.fav_quote_fragment_default_layout); - ArrayList favQuoteArrayList = dbHalp.getFavQuotes(); + if(dbHalp.getRowCount() == 0) + showDefaultFragLayout(); + else + showFavRecycler(); + // List of Fav Quotes, get it from Database + ArrayList favQuoteArrayList = dbHalp.getFavQuotes(); FavQuoteAdapter favQuoteAdapter = new FavQuoteAdapter(getContext(), favQuoteArrayList); - // Allows Recycler to perform actions on the Layout // whenever the particular adapter detects a change quoteRecyclerView.setAdapter(favQuoteAdapter); - // RecyclerView's Layout Manager // Used for viewing RecyclerView's nodes LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext()); - // Set Layout Manager for RecyclerView // Two Managers available: 1. LinearLayoutManager 2. StaggeredGridLayoutManager quoteRecyclerView.setLayoutManager(linearLayoutManager); - // Add Default ItemDecoration // Used to Decorate every RecyclerView Item // Any interaction with the Item won't affect the decoration quoteRecyclerView.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL)); - sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); if (!sharedPreferences.getBoolean("IS_DELETE_QUOTE_SHOWN", false)) { // Instruct the user @@ -86,6 +92,16 @@ public void onClick(View view) { return view; } + public static void showDefaultFragLayout() { + favQuoteDefaultLayout.setVisibility(View.VISIBLE); + quoteRecyclerView.setVisibility(View.GONE); + } + + public static void showFavRecycler() { + favQuoteDefaultLayout.setVisibility(View.GONE); + quoteRecyclerView.setVisibility(View.VISIBLE); + } + @Override public void onDestroyView() { if (snackbar != null) diff --git a/app/src/main/java/com/arvind/quote/fragment/GibQuoteFragment.java b/app/src/main/java/com/arvind/quote/fragment/GibQuoteFragment.java index b2a1abd..167bc26 100644 --- a/app/src/main/java/com/arvind/quote/fragment/GibQuoteFragment.java +++ b/app/src/main/java/com/arvind/quote/fragment/GibQuoteFragment.java @@ -1,28 +1,35 @@ package com.arvind.quote.fragment; +import android.animation.ObjectAnimator; import android.content.Context; +import android.content.DialogInterface; import android.content.SharedPreferences; import android.graphics.Typeface; +import android.graphics.drawable.TransitionDrawable; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; +import android.os.Handler; import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; +import android.support.v7.app.AlertDialog; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Spinner; -import android.widget.Toast; +import android.view.WindowManager; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; import com.android.volley.Request; import com.android.volley.RequestQueue; @@ -36,7 +43,7 @@ import com.arvind.quote.adapter.QuoteAdapter; import com.arvind.quote.database.GibDatabaseHelper; import com.getkeepsafe.taptargetview.TapTarget; -import com.getkeepsafe.taptargetview.TapTargetSequence; +import com.getkeepsafe.taptargetview.TapTargetView; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -45,7 +52,7 @@ import java.util.ArrayList; import java.util.Random; -public class GibQuoteFragment extends Fragment { +public class GibQuoteFragment extends Fragment implements View.OnTouchListener { private final String TAG = "GibQuoteFragment"; @@ -55,9 +62,8 @@ public class GibQuoteFragment extends Fragment { private RecyclerView quoteRecyclerView; private RequestQueue requestQueue; - // QuoteProvider Details - private String quoteProvider; + private String quoteProvider = "Offline"; private String quoteUrl; private String quoteTextVarName; private String quoteAuthorVarName; @@ -69,6 +75,16 @@ public class GibQuoteFragment extends Fragment { private Context mContext; + private boolean areFabsShown = false; + private RelativeLayout fabRootLayout; + private LinearLayout changeProviderLayout, clearQuoteLayout; + private FloatingActionButton gibQuoteFab, changeProviderFab, clearQuoteFab; + private TextView gibQuoteInfo, changeProviderInfo, clearQuoteInfo; + private TransitionDrawable td; + private AlertDialog providerDialog; + private ObjectAnimator fabAnimator; + private LinearLayout gibFragDefaultLayout; + @Nullable @Override public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable ViewGroup container, @Nullable final Bundle savedInstanceState) { @@ -76,29 +92,39 @@ public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable ViewG View view = inflater.inflate(R.layout.gib_quote_fragment, container, false); MainActivity.setActionBarTitle("GibQuote"); - mContext = getContext(); - sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); - - String quoteJson = sharedPreferences.getString("quoteData", "null"); - - if (!quoteJson.equals("null")) { + String quoteJson = sharedPreferences.getString("quoteData", null); + // RecyclerView Object + quoteRecyclerView = view.findViewById(R.id.quote_list_view); + quoteRecyclerView.setTag("RECYCLER"); + // When no quotes are there in the RecyclerView, show this message + gibFragDefaultLayout = view.findViewById(R.id.gib_quote_fragment_default_layout); + gibFragDefaultLayout.setTag("GIBDEF"); + // The Dimmed layout which appears when FAB is long pressed + fabRootLayout = view.findViewById(R.id.fab_root_layout); + fabRootLayout.setTag("FABROOT"); + fabRootLayout.setOnTouchListener(this); + // The Animation Drawable + td = (TransitionDrawable) fabRootLayout.getBackground(); + + if (quoteJson != null) { Log.d(TAG, "Restoring 'em Quotes"); quoteArrayList = new Gson().fromJson(quoteJson, new TypeToken>() { }.getType()); + showViewByTag(quoteRecyclerView); + // RECYCLER + FABROOT } else { Log.d(TAG, "New Session. Initializing Quote List"); quoteArrayList = new ArrayList<>(); + showViewByTag(gibFragDefaultLayout); + // GIBDEF + FABROOT } // RequestQueue executes any number of requests, asynchronously // More of a Queue Manager for Volley requestQueue = Volley.newRequestQueue(mContext); - // RecyclerView Object - quoteRecyclerView = view.findViewById(R.id.quote_list_view); - // RecyclerView's Adapter - Detects change on DataSet quoteAdapter = new QuoteAdapter(mContext, quoteArrayList); @@ -125,9 +151,11 @@ public View onCreateView(@NonNull final LayoutInflater inflater, @Nullable ViewG quoteRecyclerView.addItemDecoration(new DividerItemDecoration(mContext, DividerItemDecoration.VERTICAL)); // FAB to generate quote onClick - final FloatingActionButton gibQuoteFab = view.findViewById(R.id.gib_quote_fab); + gibQuoteFab = view.findViewById(R.id.gib_quote_fab); + gibQuoteInfo = view.findViewById(R.id.gib_quote_fab_info); + gibQuoteInfo.setVisibility(View.GONE); - String[] quoteProviders; + final String[] quoteProviders; if (isNetworkAvailable()) { Log.v(TAG, "Connected to Internet"); quoteProviders = new String[]{"Offline", "Forismatic", "Talaikis", "Storm"}; @@ -149,7 +177,7 @@ public void onScrolled(RecyclerView recyclerView, int dx, int dy) { // dy - Instantaneous change in 'y' co-ordinate // Origin is at Top-Left corner // scrollDown -> Final 'y' co-ordinate is lesser than (or below) Initial 'y' co-ordinate - // which results in a positive difference + // which results in a positive difference // scrollUp -> Final 'y' Co-ordinate is greater than (or above) Initial 'y' co-ordinate // which results in a Negative difference @@ -190,51 +218,204 @@ else if (dy < 0 && gibQuoteFab.getVisibility() != View.VISIBLE) { gibQuoteFab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (quoteProvider.equals("Offline")) { - quoteArrayList.add(gibDatabaseHelper.getRandomQuote(generateRandomNumber())); - quoteAdapter.notifyItemInserted(quoteArrayList.size()); - quoteRecyclerView.smoothScrollToPosition(quoteAdapter.getItemCount() - 1); - } else { - generateStuffs(); + if(areFabsShown) { + hideFabMenu(); + showViewByTag(quoteRecyclerView); } + else { + if (quoteProvider.equals("Offline")) { + quoteArrayList.add(gibDatabaseHelper.getRandomQuote(generateRandomNumber())); + quoteAdapter.notifyItemInserted(quoteArrayList.size()); + quoteRecyclerView.smoothScrollToPosition(quoteAdapter.getItemCount() - 1); + } else { + generateStuffs(); + } + } + } + }); + + gibQuoteFab.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View view) { + if(areFabsShown) + hideFabMenu(); + else + showFabMenu(); + return true; } }); + ////////////// + // FAB MENU // + ////////////// + + ///////////////////////// + // CHANGE PROVIDER FAB // + ///////////////////////// + + View.OnClickListener changeProviderListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), MainActivity.themeId); + builder.setSingleChoiceItems(quoteProviders, 0, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + quoteProvider = quoteProviders[i]; + changeProvider(quoteProviders[i]); + } + }); + builder.setPositiveButton("Close", null); + builder.setTitle("Change Quote Provider"); + providerDialog = builder.create(); + WindowManager.LayoutParams lp = new WindowManager.LayoutParams(); + lp.copyFrom(providerDialog.getWindow().getAttributes()); + lp.width = WindowManager.LayoutParams.WRAP_CONTENT; + lp.height = WindowManager.LayoutParams.WRAP_CONTENT; + lp.gravity = Gravity.CENTER; + providerDialog.getWindow().setAttributes(lp); + providerDialog.show(); + } + }; + + changeProviderLayout = view.findViewById(R.id.fabtext_change_provider); + changeProviderFab = view.findViewById(R.id.fab_change_provider); + changeProviderFab.setOnClickListener(changeProviderListener); + + changeProviderInfo = view.findViewById(R.id.text_change_provider); + changeProviderInfo.setOnClickListener(changeProviderListener); + + ///////////////////// + // CLEAR QUOTE FAB // + ///////////////////// + + View.OnClickListener clearQuoteListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + quoteArrayList.clear(); + quoteAdapter.notifyDataSetChanged(); + quoteRecyclerView.removeAllViews(); + showViewByTag(gibFragDefaultLayout); + } + }; + + clearQuoteLayout = view.findViewById(R.id.fabtext_remove_quotes); + clearQuoteFab = view.findViewById(R.id.fab_remove_quotes); + clearQuoteFab.setOnClickListener(clearQuoteListener); + + clearQuoteInfo = view.findViewById(R.id.text_remove_quotes); + clearQuoteInfo.setOnClickListener(clearQuoteListener); + + // Hide the Layouts which hold these FABs and Labels, initially + changeProviderLayout.setVisibility(View.GONE); + clearQuoteLayout.setVisibility(View.GONE); + // Set their Alpha (Transparency) to 0 (Fully Transparent), for the convenience of proper animation + changeProviderInfo.setAlpha(0); + clearQuoteInfo.setAlpha(0); + // If no quotes are present if (sharedPreferences.getBoolean("IS_USER_INTRODUCED", false)) Log.i(TAG, "User knows me already!"); else { Log.i(TAG, "Let's Introduce ourselves"); showIntroTapTargets(view); + sharedPreferences.edit().putBoolean("IS_USER_INTRODUCED", true).apply(); } - // Provides user to select an entry from a list of dropdown entries - Spinner providerSpinner = view.findViewById(R.id.provider_select); - providerSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + // Animator to animate an Object, with the specified animation property + fabAnimator = ObjectAnimator.ofFloat( + gibQuoteFab, // Object to animate + "rotation", // Animation property - I wanna rotate it + 0f, 360f // Animation's initial and final values + ).setDuration(500); // Duration of animation + + return view; + } + + // Convenience method + private void showViewByTag(View viewToShow) { + quoteRecyclerView.setVisibility(View.GONE); + fabRootLayout.setVisibility(View.GONE); + gibFragDefaultLayout.setVisibility(View.GONE); + viewToShow.setVisibility(View.VISIBLE); + } + + private void showFabMenu() { + td.startTransition(200); + fabAnimator.start(); // Start the animation, it should complete in 500 seconds + // Create a new thread, which would change the icon while the animation is taking place + // In this case, I'm changing the icon in midway of animation :D + // Delay time is the second argument + new Handler().postDelayed(new Runnable() { @Override - public void onItemSelected(AdapterView adapterView, View view, int i, long l) { - quoteProvider = adapterView.getSelectedItem().toString(); - changeProvider(quoteProvider); + public void run() { + gibQuoteFab.setImageResource(R.drawable.ic_close_black_24dp); } + }, 250); + // Workaround to Disable (Interactions with) RecyclerView + // RecyclerView's item checks for this boolean + // if this is set to false, the Listener code wouldn't be executed + QuoteAdapter.isClickable = false; + // Statue! -- Make the RecyclerView non-scrollable + quoteRecyclerView.setLayoutFrozen(true); + // Get the FAB Menu up + fabRootLayout.setVisibility(View.VISIBLE); + // Animate the FAB labels + changeProviderInfo.animate().alpha(1).setDuration(200); + clearQuoteInfo.animate().alpha(1).setDuration(200); + gibQuoteInfo.animate().alpha(1).setDuration(200); + // Make the layouts holding these labels, visible + changeProviderLayout.setVisibility(View.VISIBLE); + clearQuoteLayout.setVisibility(View.VISIBLE); + gibQuoteInfo.setVisibility(View.VISIBLE); + // Translate these layouts to specified positions + changeProviderLayout.animate().translationYBy(-190); + clearQuoteLayout.animate().translationYBy(-360); + // Fabs are shown, make the back + areFabsShown = true; + } + + // Quite opposite of showFabMenu, except for Animating labels + private void hideFabMenu() { + td.reverseTransition(200); + fabAnimator.reverse(); + + new Handler().postDelayed(new Runnable() { @Override - public void onNothingSelected(AdapterView adapterView) { + public void run() { + gibQuoteFab.setImageResource(R.drawable.ic_format_quote_black_24dp); + } + }, 250); + + QuoteAdapter.isClickable = true; + quoteRecyclerView.setLayoutFrozen(false); + // Animation calls are asynchronous. So hide the Small FAB layouts, ONLY after the + // animation is complete + changeProviderInfo.animate().alpha(0).setDuration(200).withEndAction(new Runnable() { + @Override + public void run() { + changeProviderLayout.setVisibility(View.GONE); + } + }); + clearQuoteInfo.animate().alpha(0).setDuration(200).withEndAction(new Runnable() { + @Override + public void run() { + clearQuoteLayout.setVisibility(View.GONE); + } + }); + gibQuoteInfo.animate().alpha(0).setDuration(200).withEndAction(new Runnable() { + @Override + public void run() { + gibQuoteInfo.setVisibility(View.GONE); + fabRootLayout.setVisibility(View.GONE); } }); - // Adapter which would provide the Dropdown elements - // And listen for changes in respective elements (select events) - ArrayAdapter providerArrayAdapter = new ArrayAdapter<>( - mContext, - R.layout.support_simple_spinner_dropdown_item, - quoteProviders - ); - - providerArrayAdapter.setDropDownViewResource(R.layout.support_simple_spinner_dropdown_item); - providerSpinner.setAdapter(providerArrayAdapter); + changeProviderLayout.animate().translationYBy(190); + clearQuoteLayout.animate().translationYBy(360); - return view; + areFabsShown = false; } // Method to change JSON parameters based on the quoteProvider selected @@ -319,9 +500,6 @@ private void parseQuote(JSONObject response) { quoteAdapter.notifyItemInserted(quoteArrayList.size()); // Scroll the RecyclerView to given position, smoothly quoteRecyclerView.smoothScrollToPosition(quoteAdapter.getItemCount() - 1); - // Experimental! Do not uncomment xD - // NotificationUtils.getInstance(getContext()).issueNotification(new Quote(quoteText, authorText)); - } catch (Exception e) { e.printStackTrace(); } @@ -348,26 +526,9 @@ private void showIntroTapTargets(View view) { .createFromAsset(getActivity().getAssets(), "fonts/comfortaa/comfortaa_bold.ttf"); - TapTarget providerTapTarget = TapTarget.forView(view.findViewById(R.id.provider_select), - "Get started", - "Select Provider from which you'd fetch quotes") - .outerCircleColor(R.color.colorPrimary) - .outerCircleAlpha(0.96f) - .targetCircleColor(android.R.color.white) - .titleTextSize(32) - .titleTextColor(android.R.color.white) - .descriptionTextSize(20) - .descriptionTextColor(R.color.colorGray) - .titleTypeface(boldTypeFace) - .dimColor(android.R.color.black) - .drawShadow(true) - .cancelable(true) - .transparentTarget(true) - .targetRadius(90); - TapTarget gibQuoteTapTarget = TapTarget.forView(view.findViewById(R.id.gib_quote_fab), - "One last step", - "Press to fetch a quote!") + "Get Started", + "Single tap to fetch a quote\nLong Press to view Options") .outerCircleColor(R.color.colorPrimary) .outerCircleAlpha(0.96f) .targetCircleColor(android.R.color.white) @@ -382,33 +543,7 @@ private void showIntroTapTargets(View view) { .transparentTarget(true) .targetRadius(60); - new TapTargetSequence(getActivity()) - .targets(providerTapTarget, gibQuoteTapTarget) - .listener(new TapTargetSequence.Listener() { - @Override - public void onSequenceFinish() { - Log.i(TAG, "Introduction complete, start Gibbing Quotes"); - Toast.makeText(mContext, "You're good to go!", Toast.LENGTH_LONG).show(); - sharedPreferences.edit() - .putBoolean("IS_USER_INTRODUCED", true) - .apply(); - } - - @Override - public void onSequenceStep(TapTarget lastTarget, boolean targetClicked) { - if (!targetClicked) { - // Seems the user knows everything - sharedPreferences.edit() - .putBoolean("IS_USER_INTRODUCED", true) - .apply(); - } - } - - @Override - public void onSequenceCanceled(TapTarget lastTarget) { - Log.d(TAG, "Intro Sequence Cancelled"); - } - }).start(); + TapTargetView.showFor(getActivity(), gibQuoteTapTarget); } @Override @@ -417,10 +552,33 @@ public void onDestroyView() { Log.d(TAG, "Fragment onDestroyView called"); // GSON can convert De-serialized Data (Java Objects) to Serialized Data (JSON) and vice-versa // https://github.com/google/gson - String quoteJson = new Gson().toJson(quoteArrayList); - // Save the serialized JSON String as a Preference - sharedPreferences.edit() - .putString("quoteData", quoteJson) - .apply(); + if(!quoteArrayList.isEmpty()) { + String quoteJson = new Gson().toJson(quoteArrayList); + // Save the serialized JSON String as a Preference + sharedPreferences.edit() + .putString("quoteData", quoteJson) + .apply(); + } else + sharedPreferences.edit().putString("quoteData", null).apply(); + // Hide the 'Change Provider' Dialog + if(providerDialog != null) + providerDialog.dismiss(); + } + + @Override + public boolean onTouch(View view, MotionEvent motionEvent) { + // If the touch event is on fabRootLayout + if(view.getTag().equals("FABROOT")) { + // If the fabMenu is opened and the gesture performed on fabRootLayout is complete + if (areFabsShown && motionEvent.getAction() == MotionEvent.ACTION_UP) { + // Acknowledge the event by hiding the fabMenu + Log.d(TAG, "Gesture performed in FABROOT is complete"); + Log.d(TAG, "Hiding FAB Menu"); + view.performClick(); + hideFabMenu(); + return false; + } + } + return true; } } diff --git a/app/src/main/java/com/arvind/quote/services/CommonUtilService.java b/app/src/main/java/com/arvind/quote/services/CommonUtilService.java new file mode 100644 index 0000000..1bb7707 --- /dev/null +++ b/app/src/main/java/com/arvind/quote/services/CommonUtilService.java @@ -0,0 +1,76 @@ +package com.arvind.quote.services; + +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.Nullable; +import android.util.Log; +import android.widget.Toast; + +import com.arvind.quote.adapter.Quote; +import com.arvind.quote.utils.CommonUtils; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +public class CommonUtilService extends IntentService { + + // Intent Keys + public static final String INTENT_EXEC_KEY = "toExecute"; + public static final String SHARE_QUOTE = "shareQuote"; + public static final String ADD_FAV_QUOTE = "addFavQuote"; + public static final String REMOVE_FAV_QUOTE = "removeFavQuote"; + public static final String QUOTE_KEY = "quote"; + // IMPORTANT + // This is an Utility Class + // Unless when it handles an Intent, then it is a Service + private static final String TAG = "CommonUtilService"; + public static Handler handler = new Handler(Looper.getMainLooper()); + private static boolean isActivityOpen = true; + + public CommonUtilService() { + super("CommonUtilService"); + } + + public static void showToast(final Context context, final String toastText) { + if (!isActivityOpen) { + handler.post(new Runnable() { + @Override + public void run() { + Log.d(TAG, "Toast posted in new Handler thread"); + Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show(); + } + }); + } else { + Toast.makeText(context, toastText, Toast.LENGTH_SHORT).show(); + } + } + + @Override + protected void onHandleIntent(@Nullable Intent intent) { + // GSON Ftw! + Quote quote = new Gson().fromJson( + intent.getStringExtra(QUOTE_KEY), + new TypeToken() { + }.getType() + ); + Log.d(TAG, "Ohai there!"); + // This service runs in Background, set this to false + isActivityOpen = false; + switch (intent.getStringExtra(INTENT_EXEC_KEY)) { + case SHARE_QUOTE: + CommonUtils.shareQuote(this, quote); + break; + case ADD_FAV_QUOTE: + CommonUtils.addToFavQuoteList(this, quote); + break; + case REMOVE_FAV_QUOTE: + CommonUtils.removeFromFavQuotesList(this, quote.getId()); + break; + default: + Log.d(TAG, "Deb has gone buggy"); + } + } + +} diff --git a/app/src/main/java/com/arvind/quote/utils/CommonUtils.java b/app/src/main/java/com/arvind/quote/utils/CommonUtils.java new file mode 100644 index 0000000..d4dad0a --- /dev/null +++ b/app/src/main/java/com/arvind/quote/utils/CommonUtils.java @@ -0,0 +1,49 @@ +package com.arvind.quote.utils; + +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +import com.arvind.quote.adapter.Quote; +import com.arvind.quote.database.FavDatabaseHelper; + +import static com.arvind.quote.services.CommonUtilService.showToast; + +public class CommonUtils { + + private static final String TAG = "CommonUtils"; + + /* Allows the user to share currently displayed quote */ + public static void shareQuote(Context context, Quote quote) { + Log.d(TAG, "shareQuote: Creating Share Intent"); + // My intention is to send (throw) a piece of Text (ball) + Intent quoteIntent = new Intent(Intent.ACTION_SEND); + // Piece of Text (the Ball) + String quoteMessage = quote.getQuoteText() + "\n\n-- " + quote.getAuthorText(); + // Specify the Text to be thrown + quoteIntent.putExtra(Intent.EXTRA_TEXT, quoteMessage); + // Specify the MIME type of the object to be thrown + quoteIntent.setType("text/plain"); + // Send an Acknowledgement + showToast(context, "Select an App to GibQuote"); + // Throw the Ball! + context.startActivity(Intent.createChooser(quoteIntent, "Share this Quote")); + } + + public static int addToFavQuoteList(Context context, Quote quoteData) { + FavDatabaseHelper favDatabaseHelper = FavDatabaseHelper.getInstance(context); + int id = (int) favDatabaseHelper.getRowCount(); + Log.d(TAG, "addToFavQuoteList: Inserting FavQuote " + id); + favDatabaseHelper.addFavQuote(id, quoteData); + showToast(context, "Added to Favorites"); + return id; + } + + public static void removeFromFavQuotesList(Context context, int id) { + FavDatabaseHelper favDatabaseHelper = FavDatabaseHelper.getInstance(context); + Log.d(TAG, "removeFromFavQuotesList: Removing FavQuote " + id); + showToast(context, "Removed from Favorites"); + favDatabaseHelper.removeFavQuote(id); + } + +} diff --git a/app/src/main/res/anim/slide_in_left.xml b/app/src/main/res/anim/slide_in_left.xml new file mode 100644 index 0000000..9de12a8 --- /dev/null +++ b/app/src/main/res/anim/slide_in_left.xml @@ -0,0 +1,3 @@ + + diff --git a/app/src/main/res/anim/slide_out_right.xml b/app/src/main/res/anim/slide_out_right.xml new file mode 100644 index 0000000..349b480 --- /dev/null +++ b/app/src/main/res/anim/slide_out_right.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/fav_frag_image.xml b/app/src/main/res/drawable/fav_frag_image.xml new file mode 100644 index 0000000..315e355 --- /dev/null +++ b/app/src/main/res/drawable/fav_frag_image.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_close_black_24dp.xml b/app/src/main/res/drawable/ic_close_black_24dp.xml new file mode 100644 index 0000000..e959aab --- /dev/null +++ b/app/src/main/res/drawable/ic_close_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_delete_black_24dp.xml b/app/src/main/res/drawable/ic_delete_black_24dp.xml new file mode 100644 index 0000000..18c0df6 --- /dev/null +++ b/app/src/main/res/drawable/ic_delete_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_format_quote_black_24dp.xml b/app/src/main/res/drawable/ic_format_quote_black_24dp.xml new file mode 100644 index 0000000..69e7d0a --- /dev/null +++ b/app/src/main/res/drawable/ic_format_quote_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_shuffle_black_24dp.xml b/app/src/main/res/drawable/ic_shuffle_black_24dp.xml new file mode 100644 index 0000000..6f5f6fe --- /dev/null +++ b/app/src/main/res/drawable/ic_shuffle_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/round.xml b/app/src/main/res/drawable/round.xml index 524d1ce..2eaf444 100644 --- a/app/src/main/res/drawable/round.xml +++ b/app/src/main/res/drawable/round.xml @@ -4,12 +4,12 @@ android:width="0dp" android:color="@android:color/transparent" /> - + - + \ No newline at end of file diff --git a/app/src/main/res/drawable/transition.xml b/app/src/main/res/drawable/transition.xml new file mode 100644 index 0000000..d0971bf --- /dev/null +++ b/app/src/main/res/drawable/transition.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fav_quote_fragment.xml b/app/src/main/res/layout/fav_quote_fragment.xml index 4dd25aa..f6dc3f6 100644 --- a/app/src/main/res/layout/fav_quote_fragment.xml +++ b/app/src/main/res/layout/fav_quote_fragment.xml @@ -8,4 +8,32 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> + + + + + + + + + + diff --git a/app/src/main/res/layout/gib_quote_fragment.xml b/app/src/main/res/layout/gib_quote_fragment.xml index 0d9fb76..0bed5fa 100644 --- a/app/src/main/res/layout/gib_quote_fragment.xml +++ b/app/src/main/res/layout/gib_quote_fragment.xml @@ -4,53 +4,160 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + android:layout_height="match_parent" /> + + + + + android:text="Want some quotes?" + android:textSize="22sp" /> - + android:padding="8dp" + android:text="Single tap the FAB to get one" + android:textStyle="italic" /> + - + android:layout_height="match_parent" + android:background="@drawable/transition"> + + - + + + + + + + + + + + + + + + + + android:gravity="center" + android:orientation="horizontal"> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/drawer_view.xml b/app/src/main/res/menu/drawer_view.xml index 941bdde..10ab99e 100644 --- a/app/src/main/res/menu/drawer_view.xml +++ b/app/src/main/res/menu/drawer_view.xml @@ -4,7 +4,7 @@ ?colorAccent + + + + \ No newline at end of file