diff --git a/app/build.gradle b/app/build.gradle index d3c4aae..39cf9b2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,4 +26,5 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' + compile project(path: ':smartrate') } diff --git a/app/src/main/java/guy/rateapplication/MainActivity.java b/app/src/main/java/guy/rateapplication/MainActivity.java index 2bde310..4e26532 100644 --- a/app/src/main/java/guy/rateapplication/MainActivity.java +++ b/app/src/main/java/guy/rateapplication/MainActivity.java @@ -28,6 +28,8 @@ import android.widget.TextView; import android.widget.Toast; +import guy4444.smartrate.SmartRate; + public class MainActivity extends AppCompatActivity { @Override @@ -41,7 +43,7 @@ protected void onCreate(Bundle savedInstanceState) { fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - Rate(MainActivity.this + SmartRate.Rate(MainActivity.this , "Rate Us" , "Tell others what you think about this app" , "Continue" @@ -54,221 +56,8 @@ public void onClick(View view) { } }); - saveLastAskTime(this, 0); + //saveLastAskTime(this, 0); } - private static final long DONT_ASK_AGAIN_VALUE = -1; - private static final long TIME_BETWEEN_DIALOG_MS = 1000l * 20 * 1; - private static final String SP_LIBRARY_NAME = "SP_RATE_LIBRARY"; - private static final String SP_KEY_LAST_ASK_TIME = "SP_KEY_LAST_ASK_TIME"; - private static int selectedStar = 1; - private static String DEFAULT_TEXT_TITLE = "Rate Us"; - private static String DEFAULT_TEXT_CONTENT = "Tell others what you think about this app"; - private static String DEFAULT_TEXT_OK = "Continue"; - private static String DEFAULT_TEXT_LATER = "Ask me later"; - private static String DEFAULT_TEXT_STOP = "Never ask again"; - private static String DEFAULT_TEXT_THANKS = "Thanks for the feedback"; - - private static String shadeColor(String color, int percent) { - - int R = Integer.parseInt(color.substring(1,3),16); - int G = Integer.parseInt(color.substring(3,5),16); - int B = Integer.parseInt(color.substring(5,7),16); - - R = R * (100 + percent) / 100; - G = G * (100 + percent) / 100; - B = B * (100 + percent) / 100; - - R = (R<255)?R:255; - G = (G<255)?G:255; - B = (B<255)?B:255; - - String RR = (Integer.toString(R, 16).length()==1) ? "0" + Integer.toString(R, 16) : Integer.toString(R, 16); - String GG = (Integer.toString(G, 16).length()==1) ? "0" + Integer.toString(G, 16) : Integer.toString(G, 16); - String BB = (Integer.toString(B, 16).length()==1) ? "0" + Integer.toString(B, 16) : Integer.toString(B, 16); - - return "#"+RR+GG+BB; - } - - public static void Rate( - final Activity activity - , final String _title - , final String _content - , final String _ok_text - , final String _later_text - , final String _stop_text - , final String _thanksForFeedback - , final int mainColor - , final int openStoreFromXStars - ) { - - - final String title = (_title!=null && !_title.equals("")) ? _title : DEFAULT_TEXT_TITLE; - final String content = (_content!=null && !_content.equals("")) ? _content : DEFAULT_TEXT_CONTENT; - final String ok_text = (_ok_text!=null && !_ok_text.equals("")) ? _ok_text : DEFAULT_TEXT_OK; - final String later_text = (_later_text!=null && !_later_text.equals("")) ? _later_text : DEFAULT_TEXT_LATER; - final String stop_text = (_stop_text!=null && !_stop_text.equals("")) ? _stop_text : DEFAULT_TEXT_STOP; - final String thanksForFeedback = (_thanksForFeedback!=null && !_thanksForFeedback.equals("")) ? _thanksForFeedback : DEFAULT_TEXT_THANKS; - - if (getLastAskTime(activity) == DONT_ASK_AGAIN_VALUE) { - // user already rate or click on never ask button - return; - } - - if (System.currentTimeMillis() < getLastAskTime(activity) + TIME_BETWEEN_DIALOG_MS) { - return; - } - - saveLastAskTime(activity, System.currentTimeMillis()); - - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity); - LayoutInflater inflater = activity.getLayoutInflater(); - View dialogView = inflater.inflate(R.layout.dialog_rate, null); - dialogBuilder.setView(dialogView); - final AlertDialog alertDialog = dialogBuilder.create(); - alertDialog.setCancelable(true); - alertDialog.setCanceledOnTouchOutside(true); - - final RelativeLayout alert_LAY_back = (RelativeLayout) dialogView.findViewById(R.id.alert_LAY_back); - final AppCompatButton alert_BTN_ok = (AppCompatButton) dialogView.findViewById(R.id.alert_BTN_ok); - final Button alert_BTN_later = (Button) dialogView.findViewById(R.id.alert_BTN_later); - final Button alert_BTN_stop = (Button) dialogView.findViewById(R.id.alert_BTN_stop); - final TextView alert_LBL_title = (TextView) dialogView.findViewById(R.id.alert_LBL_title); - final TextView alert_LBL_content = (TextView) dialogView.findViewById(R.id.alert_LBL_content); - final ImageButton alert_BTN_star_1 = (ImageButton) dialogView.findViewById(R.id.alert_BTN_star_1); - final ImageButton alert_BTN_star_2 = (ImageButton) dialogView.findViewById(R.id.alert_BTN_star_2); - final ImageButton alert_BTN_star_3 = (ImageButton) dialogView.findViewById(R.id.alert_BTN_star_3); - final ImageButton alert_BTN_star_4 = (ImageButton) dialogView.findViewById(R.id.alert_BTN_star_4); - final ImageButton alert_BTN_star_5 = (ImageButton) dialogView.findViewById(R.id.alert_BTN_star_5); - final ImageButton[] stars = new ImageButton[]{alert_BTN_star_1, alert_BTN_star_2, alert_BTN_star_3, alert_BTN_star_4, alert_BTN_star_5}; - - - alert_LAY_back.setBackgroundColor(mainColor); - alert_BTN_ok.getBackground().setColorFilter(mainColor, PorterDuff.Mode.MULTIPLY); - alert_LBL_title.setTextColor(mainColor); - alert_LBL_content.setTextColor(mainColor); - alert_BTN_later.setTextColor(Color.parseColor(shadeColor(String.format("#%06X", 0xFFFFFF & mainColor), -33))); - alert_BTN_stop.setTextColor(Color.parseColor(shadeColor(String.format("#%06X", 0xFFFFFF & mainColor), -33))); - - - final int drawable_active = R.drawable.ic_star_active; - final int drawable_deactive = R.drawable.ic_star_deactive; - - View.OnClickListener starsClickListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - int clickedIndex = -1; - for (int i = 0; i < stars.length; i++) { - if (stars[i].getId() == v.getId()) { - clickedIndex = i; - break; - } - } - if (clickedIndex != -1) { - for (int i = 0; i <= clickedIndex; i++) { - stars[i].setImageResource(drawable_active); - } - for (int i = clickedIndex + 1; i < stars.length; i++) { - stars[i].setImageResource(drawable_deactive); - } - } - - alert_BTN_ok.setEnabled(true); - alert_BTN_ok.setText((clickedIndex + 1) + "/5\n" + ok_text); - selectedStar = clickedIndex + 1; - } - }; - - alert_BTN_star_1.setOnClickListener(starsClickListener); - alert_BTN_star_2.setOnClickListener(starsClickListener); - alert_BTN_star_3.setOnClickListener(starsClickListener); - alert_BTN_star_4.setOnClickListener(starsClickListener); - alert_BTN_star_5.setOnClickListener(starsClickListener); - - - - alert_LBL_title.setText(title); - alert_LBL_content.setText(content); - - - - if (ok_text!=null && !ok_text.equals("")) { - alert_BTN_ok.setText(ok_text); - alert_BTN_ok.setText("?/5\n" + ok_text); - alert_BTN_ok.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - saveLastAskTime(activity, DONT_ASK_AGAIN_VALUE); - - int _openStoreFrom_Stars = openStoreFromXStars; - if (openStoreFromXStars < 1 || openStoreFromXStars > 5) { - _openStoreFrom_Stars = 1; - } - if (selectedStar >= _openStoreFrom_Stars) { - launchMarket(activity); - } else { - Toast.makeText(activity, thanksForFeedback, Toast.LENGTH_SHORT).show(); - } - alertDialog.dismiss(); - } - }); - } - else { - alert_BTN_ok.setVisibility(View.INVISIBLE); - } - alert_BTN_ok.setEnabled(false); - - if (later_text!=null && !later_text.equals("")) { - alert_BTN_later.setText(later_text); - alert_BTN_later.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - - alertDialog.dismiss(); - } - }); - } - else { - alert_BTN_later.setVisibility(View.INVISIBLE); - } - - if (stop_text!=null && !stop_text.equals("")) { - alert_BTN_stop.setText(stop_text); - alert_BTN_stop.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - saveLastAskTime(activity, DONT_ASK_AGAIN_VALUE); - alertDialog.dismiss(); - } - }); - } - else { - alert_BTN_stop.setVisibility(View.INVISIBLE); - } - - alertDialog.show(); - } - - private static void launchMarket(Activity activity) { - Uri uri = Uri.parse("market://details?id=" + activity.getPackageName()); - Intent myAppLinkToMarket = new Intent(Intent.ACTION_VIEW, uri); - try { - activity.startActivity(myAppLinkToMarket); - } catch (ActivityNotFoundException e) { - Toast.makeText(activity, " unable to find google play app", Toast.LENGTH_LONG).show(); - } - } - - private static long getLastAskTime(Activity activity) { - SharedPreferences sharedPreferences = activity.getSharedPreferences(SP_LIBRARY_NAME, Context.MODE_PRIVATE); - long val = sharedPreferences.getLong(SP_KEY_LAST_ASK_TIME, 0); - return val; - } - - private static void saveLastAskTime(Activity activity, long time) { - SharedPreferences.Editor editor = activity.getSharedPreferences(SP_LIBRARY_NAME, Context.MODE_PRIVATE).edit(); - editor.putLong(SP_KEY_LAST_ASK_TIME, time); - editor.apply(); - } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index eed4d89..339231b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -28,6 +28,6 @@ android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" - app:srcCompat="@android:drawable/ic_dialog_email" /> + app:srcCompat="@android:drawable/star_off" /> \ No newline at end of file diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index 3fc2170..40d655d 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -11,7 +11,7 @@ Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getTargetContext(); + + assertEquals("guy4444.smartrate.test", appContext.getPackageName()); + } +} diff --git a/smartrate/src/main/AndroidManifest.xml b/smartrate/src/main/AndroidManifest.xml new file mode 100644 index 0000000..24a206f --- /dev/null +++ b/smartrate/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/smartrate/src/main/java/guy4444/smartrate/SmartRate.java b/smartrate/src/main/java/guy4444/smartrate/SmartRate.java new file mode 100644 index 0000000..a3bfaa0 --- /dev/null +++ b/smartrate/src/main/java/guy4444/smartrate/SmartRate.java @@ -0,0 +1,237 @@ +package guy4444.smartrate; + +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.net.Uri; +import android.support.v7.app.AlertDialog; +import android.support.v7.widget.AppCompatButton; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; +import android.widget.ImageButton; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +public class SmartRate { + + private static final long DONT_ASK_AGAIN_VALUE = -1; + private static final long TIME_BETWEEN_DIALOG_MS = 1000l * 20 * 1; + private static final String SP_LIBRARY_NAME = "SP_RATE_LIBRARY"; + private static final String SP_KEY_LAST_ASK_TIME = "SP_KEY_LAST_ASK_TIME"; + private static int selectedStar = 1; + private static String DEFAULT_TEXT_TITLE = "Rate Us"; + private static String DEFAULT_TEXT_CONTENT = "Tell others what you think about this app"; + private static String DEFAULT_TEXT_OK = "Continue"; + private static String DEFAULT_TEXT_LATER = "Ask me later"; + private static String DEFAULT_TEXT_STOP = "Never ask again"; + private static String DEFAULT_TEXT_THANKS = "Thanks for the feedback"; + + private static String shadeColor(String color, int percent) { + + int R = Integer.parseInt(color.substring(1,3),16); + int G = Integer.parseInt(color.substring(3,5),16); + int B = Integer.parseInt(color.substring(5,7),16); + + R = R * (100 + percent) / 100; + G = G * (100 + percent) / 100; + B = B * (100 + percent) / 100; + + R = (R<255)?R:255; + G = (G<255)?G:255; + B = (B<255)?B:255; + + String RR = (Integer.toString(R, 16).length()==1) ? "0" + Integer.toString(R, 16) : Integer.toString(R, 16); + String GG = (Integer.toString(G, 16).length()==1) ? "0" + Integer.toString(G, 16) : Integer.toString(G, 16); + String BB = (Integer.toString(B, 16).length()==1) ? "0" + Integer.toString(B, 16) : Integer.toString(B, 16); + + return "#"+RR+GG+BB; + } + + public static void Rate( + final Activity activity + , final String _title + , final String _content + , final String _ok_text + , final String _later_text + , final String _stop_text + , final String _thanksForFeedback + , final int mainColor + , final int openStoreFromXStars + ) { + + + final String title = (_title!=null && !_title.equals("")) ? _title : DEFAULT_TEXT_TITLE; + final String content = (_content!=null && !_content.equals("")) ? _content : DEFAULT_TEXT_CONTENT; + final String ok_text = (_ok_text!=null && !_ok_text.equals("")) ? _ok_text : DEFAULT_TEXT_OK; + final String later_text = (_later_text!=null && !_later_text.equals("")) ? _later_text : DEFAULT_TEXT_LATER; + final String stop_text = (_stop_text!=null && !_stop_text.equals("")) ? _stop_text : DEFAULT_TEXT_STOP; + final String thanksForFeedback = (_thanksForFeedback!=null && !_thanksForFeedback.equals("")) ? _thanksForFeedback : DEFAULT_TEXT_THANKS; + + if (getLastAskTime(activity) == DONT_ASK_AGAIN_VALUE) { + // user already rate or click on never ask button + return; + } + + if (System.currentTimeMillis() < getLastAskTime(activity) + TIME_BETWEEN_DIALOG_MS) { + return; + } + + saveLastAskTime(activity, System.currentTimeMillis()); + + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity); + LayoutInflater inflater = activity.getLayoutInflater(); + View dialogView = inflater.inflate(R.layout.dialog_rate, null); + dialogBuilder.setView(dialogView); + final AlertDialog alertDialog = dialogBuilder.create(); + alertDialog.setCancelable(true); + alertDialog.setCanceledOnTouchOutside(true); + + final RelativeLayout alert_LAY_back = (RelativeLayout) dialogView.findViewById(R.id.alert_LAY_back); + final AppCompatButton alert_BTN_ok = (AppCompatButton) dialogView.findViewById(R.id.alert_BTN_ok); + final Button alert_BTN_later = (Button) dialogView.findViewById(R.id.alert_BTN_later); + final Button alert_BTN_stop = (Button) dialogView.findViewById(R.id.alert_BTN_stop); + final TextView alert_LBL_title = (TextView) dialogView.findViewById(R.id.alert_LBL_title); + final TextView alert_LBL_content = (TextView) dialogView.findViewById(R.id.alert_LBL_content); + final ImageButton alert_BTN_star_1 = (ImageButton) dialogView.findViewById(R.id.alert_BTN_star_1); + final ImageButton alert_BTN_star_2 = (ImageButton) dialogView.findViewById(R.id.alert_BTN_star_2); + final ImageButton alert_BTN_star_3 = (ImageButton) dialogView.findViewById(R.id.alert_BTN_star_3); + final ImageButton alert_BTN_star_4 = (ImageButton) dialogView.findViewById(R.id.alert_BTN_star_4); + final ImageButton alert_BTN_star_5 = (ImageButton) dialogView.findViewById(R.id.alert_BTN_star_5); + final ImageButton[] stars = new ImageButton[]{alert_BTN_star_1, alert_BTN_star_2, alert_BTN_star_3, alert_BTN_star_4, alert_BTN_star_5}; + + + alert_LAY_back.setBackgroundColor(mainColor); + alert_BTN_ok.getBackground().setColorFilter(mainColor, PorterDuff.Mode.MULTIPLY); + alert_LBL_title.setTextColor(mainColor); + alert_LBL_content.setTextColor(mainColor); + alert_BTN_later.setTextColor(Color.parseColor(shadeColor(String.format("#%06X", 0xFFFFFF & mainColor), -33))); + alert_BTN_stop.setTextColor(Color.parseColor(shadeColor(String.format("#%06X", 0xFFFFFF & mainColor), -33))); + + + final int drawable_active = R.drawable.ic_star_active; + final int drawable_deactive = R.drawable.ic_star_deactive; + + View.OnClickListener starsClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + int clickedIndex = -1; + for (int i = 0; i < stars.length; i++) { + if (stars[i].getId() == v.getId()) { + clickedIndex = i; + break; + } + } + + if (clickedIndex != -1) { + for (int i = 0; i <= clickedIndex; i++) { + stars[i].setImageResource(drawable_active); + } + for (int i = clickedIndex + 1; i < stars.length; i++) { + stars[i].setImageResource(drawable_deactive); + } + } + + alert_BTN_ok.setEnabled(true); + alert_BTN_ok.setText((clickedIndex + 1) + "/5\n" + ok_text); + selectedStar = clickedIndex + 1; + } + }; + + alert_BTN_star_1.setOnClickListener(starsClickListener); + alert_BTN_star_2.setOnClickListener(starsClickListener); + alert_BTN_star_3.setOnClickListener(starsClickListener); + alert_BTN_star_4.setOnClickListener(starsClickListener); + alert_BTN_star_5.setOnClickListener(starsClickListener); + + + + alert_LBL_title.setText(title); + alert_LBL_content.setText(content); + + + + if (ok_text!=null && !ok_text.equals("")) { + alert_BTN_ok.setText(ok_text); + alert_BTN_ok.setText("?/5\n" + ok_text); + alert_BTN_ok.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + saveLastAskTime(activity, DONT_ASK_AGAIN_VALUE); + + int _openStoreFrom_Stars = openStoreFromXStars; + if (openStoreFromXStars < 1 || openStoreFromXStars > 5) { + _openStoreFrom_Stars = 1; + } + if (selectedStar >= _openStoreFrom_Stars) { + launchMarket(activity); + } else { + Toast.makeText(activity, thanksForFeedback, Toast.LENGTH_SHORT).show(); + } + alertDialog.dismiss(); + } + }); + } + else { + alert_BTN_ok.setVisibility(View.INVISIBLE); + } + alert_BTN_ok.setEnabled(false); + + if (later_text!=null && !later_text.equals("")) { + alert_BTN_later.setText(later_text); + alert_BTN_later.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + + alertDialog.dismiss(); + } + }); + } + else { + alert_BTN_later.setVisibility(View.INVISIBLE); + } + + if (stop_text!=null && !stop_text.equals("")) { + alert_BTN_stop.setText(stop_text); + alert_BTN_stop.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + saveLastAskTime(activity, DONT_ASK_AGAIN_VALUE); + alertDialog.dismiss(); + } + }); + } + else { + alert_BTN_stop.setVisibility(View.INVISIBLE); + } + + alertDialog.show(); + } + + private static void launchMarket(Activity activity) { + Uri uri = Uri.parse("market://details?id=" + activity.getPackageName()); + Intent myAppLinkToMarket = new Intent(Intent.ACTION_VIEW, uri); + try { + activity.startActivity(myAppLinkToMarket); + } catch (ActivityNotFoundException e) { + Toast.makeText(activity, " unable to find google play app", Toast.LENGTH_LONG).show(); + } + } + + private static long getLastAskTime(Activity activity) { + SharedPreferences sharedPreferences = activity.getSharedPreferences(SP_LIBRARY_NAME, Context.MODE_PRIVATE); + long val = sharedPreferences.getLong(SP_KEY_LAST_ASK_TIME, 0); + return val; + } + + private static void saveLastAskTime(Activity activity, long time) { + SharedPreferences.Editor editor = activity.getSharedPreferences(SP_LIBRARY_NAME, Context.MODE_PRIVATE).edit(); + editor.putLong(SP_KEY_LAST_ASK_TIME, time); + editor.apply(); + } +} diff --git a/app/src/main/res/drawable/border.xml b/smartrate/src/main/res/drawable/border.xml similarity index 100% rename from app/src/main/res/drawable/border.xml rename to smartrate/src/main/res/drawable/border.xml diff --git a/app/src/main/res/drawable/ic_star_active.xml b/smartrate/src/main/res/drawable/ic_star_active.xml similarity index 100% rename from app/src/main/res/drawable/ic_star_active.xml rename to smartrate/src/main/res/drawable/ic_star_active.xml diff --git a/app/src/main/res/drawable/ic_star_deactive.xml b/smartrate/src/main/res/drawable/ic_star_deactive.xml similarity index 100% rename from app/src/main/res/drawable/ic_star_deactive.xml rename to smartrate/src/main/res/drawable/ic_star_deactive.xml diff --git a/app/src/main/res/layout/dialog_rate.xml b/smartrate/src/main/res/layout/dialog_rate.xml similarity index 100% rename from app/src/main/res/layout/dialog_rate.xml rename to smartrate/src/main/res/layout/dialog_rate.xml diff --git a/smartrate/src/main/res/values/colors.xml b/smartrate/src/main/res/values/colors.xml new file mode 100644 index 0000000..724432d --- /dev/null +++ b/smartrate/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #03A9F4 + #2196F3 + #8BC34A + + + #FFFFFF + #000000 + diff --git a/smartrate/src/main/res/values/strings.xml b/smartrate/src/main/res/values/strings.xml new file mode 100644 index 0000000..6c1f5ef --- /dev/null +++ b/smartrate/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + SmartRate + diff --git a/smartrate/src/test/java/guy4444/smartrate/ExampleUnitTest.java b/smartrate/src/test/java/guy4444/smartrate/ExampleUnitTest.java new file mode 100644 index 0000000..711488b --- /dev/null +++ b/smartrate/src/test/java/guy4444/smartrate/ExampleUnitTest.java @@ -0,0 +1,17 @@ +package guy4444.smartrate; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + @Test + public void addition_isCorrect() { + assertEquals(4, 2 + 2); + } +} \ No newline at end of file