Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java

Issue 2750103005: [Payments] Move journey logger to native. (Closed)
Patch Set: Addressed comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.payments; 5 package org.chromium.chrome.browser.payments;
6 6
7 import android.app.Activity; 7 import android.app.Activity;
8 import android.content.Context; 8 import android.content.Context;
9 import android.content.Intent; 9 import android.content.Intent;
10 import android.graphics.Bitmap; 10 import android.graphics.Bitmap;
(...skipping 27 matching lines...) Expand all
38 import org.chromium.chrome.browser.profiles.Profile; 38 import org.chromium.chrome.browser.profiles.Profile;
39 import org.chromium.chrome.browser.tab.Tab; 39 import org.chromium.chrome.browser.tab.Tab;
40 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver; 40 import org.chromium.chrome.browser.tabmodel.EmptyTabModelObserver;
41 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver; 41 import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
42 import org.chromium.chrome.browser.tabmodel.TabModel; 42 import org.chromium.chrome.browser.tabmodel.TabModel;
43 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType; 43 import org.chromium.chrome.browser.tabmodel.TabModel.TabSelectionType;
44 import org.chromium.chrome.browser.tabmodel.TabModelObserver; 44 import org.chromium.chrome.browser.tabmodel.TabModelObserver;
45 import org.chromium.chrome.browser.tabmodel.TabModelSelector; 45 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
46 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; 46 import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver;
47 import org.chromium.components.payments.CurrencyFormatter; 47 import org.chromium.components.payments.CurrencyFormatter;
48 import org.chromium.components.payments.JourneyLogger;
48 import org.chromium.components.payments.PaymentValidator; 49 import org.chromium.components.payments.PaymentValidator;
49 import org.chromium.components.url_formatter.UrlFormatter; 50 import org.chromium.components.url_formatter.UrlFormatter;
50 import org.chromium.content_public.browser.RenderFrameHost; 51 import org.chromium.content_public.browser.RenderFrameHost;
51 import org.chromium.content_public.browser.WebContents; 52 import org.chromium.content_public.browser.WebContents;
52 import org.chromium.content_public.browser.WebContentsStatics; 53 import org.chromium.content_public.browser.WebContentsStatics;
53 import org.chromium.mojo.system.MojoException; 54 import org.chromium.mojo.system.MojoException;
54 import org.chromium.payments.mojom.CanMakePaymentQueryResult; 55 import org.chromium.payments.mojom.CanMakePaymentQueryResult;
55 import org.chromium.payments.mojom.PaymentComplete; 56 import org.chromium.payments.mojom.PaymentComplete;
56 import org.chromium.payments.mojom.PaymentDetails; 57 import org.chromium.payments.mojom.PaymentDetails;
57 import org.chromium.payments.mojom.PaymentDetailsModifier; 58 import org.chromium.payments.mojom.PaymentDetailsModifier;
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
251 private final RenderFrameHost mRenderFrameHost; 252 private final RenderFrameHost mRenderFrameHost;
252 private final WebContents mWebContents; 253 private final WebContents mWebContents;
253 private final String mSchemelessOriginForPaymentApp; 254 private final String mSchemelessOriginForPaymentApp;
254 private final String mOriginForDisplay; 255 private final String mOriginForDisplay;
255 private final String mSchemelessIFrameOriginForPaymentApp; 256 private final String mSchemelessIFrameOriginForPaymentApp;
256 private final String mMerchantName; 257 private final String mMerchantName;
257 @Nullable 258 @Nullable
258 private final byte[][] mCertificateChain; 259 private final byte[][] mCertificateChain;
259 private final AddressEditor mAddressEditor; 260 private final AddressEditor mAddressEditor;
260 private final CardEditor mCardEditor; 261 private final CardEditor mCardEditor;
261 private final PaymentRequestJourneyLogger mJourneyLogger = new PaymentReques tJourneyLogger(); 262 private final JourneyLogger mJourneyLogger = new JourneyLogger();
262 private final boolean mIsIncognito; 263 private final boolean mIsIncognito;
263 264
264 private PaymentRequestClient mClient; 265 private PaymentRequestClient mClient;
265 private boolean mIsCurrentPaymentRequestShowing; 266 private boolean mIsCurrentPaymentRequestShowing;
266 267
267 /** 268 /**
268 * The raw total amount being charged, as it was received from the website. This data is passed 269 * The raw total amount being charged, as it was received from the website. This data is passed
269 * to the payment app. 270 * to the payment app.
270 */ 271 */
271 private PaymentItem mRawTotal; 272 private PaymentItem mRawTotal;
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after
518 for (int i = 0; i < addresses.size(); ++i) { 519 for (int i = 0; i < addresses.size(); ++i) {
519 String countryCode = AutofillAddress.getCountryCode(addresses.get(i) .getProfile()); 520 String countryCode = AutofillAddress.getCountryCode(addresses.get(i) .getProfile());
520 if (!uniqueCountryCodes.contains(countryCode)) { 521 if (!uniqueCountryCodes.contains(countryCode)) {
521 uniqueCountryCodes.add(countryCode); 522 uniqueCountryCodes.add(countryCode);
522 PersonalDataManager.getInstance().loadRulesForRegion(countryCode ); 523 PersonalDataManager.getInstance().loadRulesForRegion(countryCode );
523 } 524 }
524 } 525 }
525 526
526 // Log the number of suggested shipping addresses. 527 // Log the number of suggested shipping addresses.
527 mJourneyLogger.setNumberOfSuggestionsShown( 528 mJourneyLogger.setNumberOfSuggestionsShown(
528 PaymentRequestJourneyLogger.SECTION_SHIPPING_ADDRESS, addresses. size()); 529 JourneyLogger.SECTION_SHIPPING_ADDRESS, addresses.size());
529 530
530 // Automatically select the first address if one is complete and if the merchant does 531 // Automatically select the first address if one is complete and if the merchant does
531 // not require a shipping address to calculate shipping costs. 532 // not require a shipping address to calculate shipping costs.
532 int firstCompleteAddressIndex = SectionInformation.NO_SELECTION; 533 int firstCompleteAddressIndex = SectionInformation.NO_SELECTION;
533 if (mUiShippingOptions.getSelectedItem() != null && !addresses.isEmpty() 534 if (mUiShippingOptions.getSelectedItem() != null && !addresses.isEmpty()
534 && addresses.get(0).isComplete()) { 535 && addresses.get(0).isComplete()) {
535 firstCompleteAddressIndex = 0; 536 firstCompleteAddressIndex = 0;
536 537
537 // The initial label for the selected shipping address should not in clude the 538 // The initial label for the selected shipping address should not in clude the
538 // country. 539 // country.
(...skipping 376 matching lines...) Expand 10 before | Expand all | Expand 10 after
915 }); 916 });
916 } 917 }
917 918
918 @Override 919 @Override
919 @PaymentRequestUI.SelectionResult 920 @PaymentRequestUI.SelectionResult
920 public int onSectionOptionSelected(@PaymentRequestUI.DataType int optionType , 921 public int onSectionOptionSelected(@PaymentRequestUI.DataType int optionType ,
921 PaymentOption option, Callback<PaymentInformation> callback) { 922 PaymentOption option, Callback<PaymentInformation> callback) {
922 if (optionType == PaymentRequestUI.TYPE_SHIPPING_ADDRESSES) { 923 if (optionType == PaymentRequestUI.TYPE_SHIPPING_ADDRESSES) {
923 assert option instanceof AutofillAddress; 924 assert option instanceof AutofillAddress;
924 // Log the change of shipping address. 925 // Log the change of shipping address.
925 mJourneyLogger.incrementSelectionChanges( 926 mJourneyLogger.incrementSelectionChanges(JourneyLogger.SECTION_SHIPP ING_ADDRESS);
926 PaymentRequestJourneyLogger.SECTION_SHIPPING_ADDRESS);
927 AutofillAddress address = (AutofillAddress) option; 927 AutofillAddress address = (AutofillAddress) option;
928 if (address.isComplete()) { 928 if (address.isComplete()) {
929 mShippingAddressesSection.setSelectedItem(option); 929 mShippingAddressesSection.setSelectedItem(option);
930 // This updates the line items and the shipping options asynchro nously. 930 // This updates the line items and the shipping options asynchro nously.
931 mClient.onShippingAddressChange(address.toPaymentAddress()); 931 mClient.onShippingAddressChange(address.toPaymentAddress());
932 } else { 932 } else {
933 editAddress(address); 933 editAddress(address);
934 } 934 }
935 mPaymentInformationCallback = callback; 935 mPaymentInformationCallback = callback;
936 return PaymentRequestUI.SELECTION_RESULT_ASYNCHRONOUS_VALIDATION; 936 return PaymentRequestUI.SELECTION_RESULT_ASYNCHRONOUS_VALIDATION;
937 } else if (optionType == PaymentRequestUI.TYPE_SHIPPING_OPTIONS) { 937 } else if (optionType == PaymentRequestUI.TYPE_SHIPPING_OPTIONS) {
938 // This may update the line items. 938 // This may update the line items.
939 mUiShippingOptions.setSelectedItem(option); 939 mUiShippingOptions.setSelectedItem(option);
940 mClient.onShippingOptionChange(option.getIdentifier()); 940 mClient.onShippingOptionChange(option.getIdentifier());
941 mPaymentInformationCallback = callback; 941 mPaymentInformationCallback = callback;
942 return PaymentRequestUI.SELECTION_RESULT_ASYNCHRONOUS_VALIDATION; 942 return PaymentRequestUI.SELECTION_RESULT_ASYNCHRONOUS_VALIDATION;
943 } else if (optionType == PaymentRequestUI.TYPE_CONTACT_DETAILS) { 943 } else if (optionType == PaymentRequestUI.TYPE_CONTACT_DETAILS) {
944 assert option instanceof AutofillContact; 944 assert option instanceof AutofillContact;
945 // Log the change of contact info. 945 // Log the change of contact info.
946 mJourneyLogger.incrementSelectionChanges( 946 mJourneyLogger.incrementSelectionChanges(JourneyLogger.SECTION_CONTA CT_INFO);
947 PaymentRequestJourneyLogger.SECTION_CONTACT_INFO);
948 AutofillContact contact = (AutofillContact) option; 947 AutofillContact contact = (AutofillContact) option;
949 948
950 if (contact.isComplete()) { 949 if (contact.isComplete()) {
951 mContactSection.setSelectedItem(option); 950 mContactSection.setSelectedItem(option);
952 } else { 951 } else {
953 editContact(contact); 952 editContact(contact);
954 return PaymentRequestUI.SELECTION_RESULT_EDITOR_LAUNCH; 953 return PaymentRequestUI.SELECTION_RESULT_EDITOR_LAUNCH;
955 } 954 }
956 } else if (optionType == PaymentRequestUI.TYPE_PAYMENT_METHODS) { 955 } else if (optionType == PaymentRequestUI.TYPE_PAYMENT_METHODS) {
957 assert option instanceof PaymentInstrument; 956 assert option instanceof PaymentInstrument;
958 if (option instanceof AutofillPaymentInstrument) { 957 if (option instanceof AutofillPaymentInstrument) {
959 // Log the change of credit card. 958 // Log the change of credit card.
960 mJourneyLogger.incrementSelectionChanges( 959 mJourneyLogger.incrementSelectionChanges(JourneyLogger.SECTION_C REDIT_CARDS);
961 PaymentRequestJourneyLogger.SECTION_CREDIT_CARDS);
962 AutofillPaymentInstrument card = (AutofillPaymentInstrument) opt ion; 960 AutofillPaymentInstrument card = (AutofillPaymentInstrument) opt ion;
963 961
964 if (!card.isComplete()) { 962 if (!card.isComplete()) {
965 editCard(card); 963 editCard(card);
966 return PaymentRequestUI.SELECTION_RESULT_EDITOR_LAUNCH; 964 return PaymentRequestUI.SELECTION_RESULT_EDITOR_LAUNCH;
967 } 965 }
968 } 966 }
969 967
970 updateOrderSummary((PaymentInstrument) option); 968 updateOrderSummary((PaymentInstrument) option);
971 mPaymentMethodsSection.setSelectedItem(option); 969 mPaymentMethodsSection.setSelectedItem(option);
(...skipping 30 matching lines...) Expand all
1002 } 1000 }
1003 1001
1004 @Override 1002 @Override
1005 @PaymentRequestUI.SelectionResult 1003 @PaymentRequestUI.SelectionResult
1006 public int onSectionAddOption( 1004 public int onSectionAddOption(
1007 @PaymentRequestUI.DataType int optionType, Callback<PaymentInformati on> callback) { 1005 @PaymentRequestUI.DataType int optionType, Callback<PaymentInformati on> callback) {
1008 if (optionType == PaymentRequestUI.TYPE_SHIPPING_ADDRESSES) { 1006 if (optionType == PaymentRequestUI.TYPE_SHIPPING_ADDRESSES) {
1009 editAddress(null); 1007 editAddress(null);
1010 mPaymentInformationCallback = callback; 1008 mPaymentInformationCallback = callback;
1011 // Log the add of shipping address. 1009 // Log the add of shipping address.
1012 mJourneyLogger.incrementSelectionAdds( 1010 mJourneyLogger.incrementSelectionAdds(JourneyLogger.SECTION_SHIPPING _ADDRESS);
1013 PaymentRequestJourneyLogger.SECTION_SHIPPING_ADDRESS);
1014 return PaymentRequestUI.SELECTION_RESULT_ASYNCHRONOUS_VALIDATION; 1011 return PaymentRequestUI.SELECTION_RESULT_ASYNCHRONOUS_VALIDATION;
1015 } else if (optionType == PaymentRequestUI.TYPE_CONTACT_DETAILS) { 1012 } else if (optionType == PaymentRequestUI.TYPE_CONTACT_DETAILS) {
1016 editContact(null); 1013 editContact(null);
1017 // Log the add of contact info. 1014 // Log the add of contact info.
1018 mJourneyLogger.incrementSelectionAdds(PaymentRequestJourneyLogger.SE CTION_CONTACT_INFO); 1015 mJourneyLogger.incrementSelectionAdds(JourneyLogger.SECTION_CONTACT_ INFO);
1019 return PaymentRequestUI.SELECTION_RESULT_EDITOR_LAUNCH; 1016 return PaymentRequestUI.SELECTION_RESULT_EDITOR_LAUNCH;
1020 } else if (optionType == PaymentRequestUI.TYPE_PAYMENT_METHODS) { 1017 } else if (optionType == PaymentRequestUI.TYPE_PAYMENT_METHODS) {
1021 editCard(null); 1018 editCard(null);
1022 // Log the add of credit card. 1019 // Log the add of credit card.
1023 mJourneyLogger.incrementSelectionAdds(PaymentRequestJourneyLogger.SE CTION_CREDIT_CARDS); 1020 mJourneyLogger.incrementSelectionAdds(JourneyLogger.SECTION_CREDIT_C ARDS);
1024 return PaymentRequestUI.SELECTION_RESULT_EDITOR_LAUNCH; 1021 return PaymentRequestUI.SELECTION_RESULT_EDITOR_LAUNCH;
1025 } 1022 }
1026 1023
1027 return PaymentRequestUI.SELECTION_RESULT_NONE; 1024 return PaymentRequestUI.SELECTION_RESULT_NONE;
1028 } 1025 }
1029 1026
1030 private void editAddress(final AutofillAddress toEdit) { 1027 private void editAddress(final AutofillAddress toEdit) {
1031 if (toEdit != null) { 1028 if (toEdit != null) {
1032 // Log the edit of a shipping address. 1029 // Log the edit of a shipping address.
1033 mJourneyLogger.incrementSelectionEdits( 1030 mJourneyLogger.incrementSelectionEdits(JourneyLogger.SECTION_SHIPPIN G_ADDRESS);
1034 PaymentRequestJourneyLogger.SECTION_SHIPPING_ADDRESS);
1035 } 1031 }
1036 mAddressEditor.edit(toEdit, new Callback<AutofillAddress>() { 1032 mAddressEditor.edit(toEdit, new Callback<AutofillAddress>() {
1037 @Override 1033 @Override
1038 public void onResult(AutofillAddress editedAddress) { 1034 public void onResult(AutofillAddress editedAddress) {
1039 if (mUI == null) return; 1035 if (mUI == null) return;
1040 1036
1041 if (editedAddress != null) { 1037 if (editedAddress != null) {
1042 // Sets or updates the shipping address label. 1038 // Sets or updates the shipping address label.
1043 editedAddress.setShippingAddressLabelWithCountry(); 1039 editedAddress.setShippingAddressLabelWithCountry();
1044 1040
(...skipping 29 matching lines...) Expand all
1074 } else { 1070 } else {
1075 providePaymentInformation(); 1071 providePaymentInformation();
1076 } 1072 }
1077 } 1073 }
1078 }); 1074 });
1079 } 1075 }
1080 1076
1081 private void editContact(final AutofillContact toEdit) { 1077 private void editContact(final AutofillContact toEdit) {
1082 if (toEdit != null) { 1078 if (toEdit != null) {
1083 // Log the edit of a contact info. 1079 // Log the edit of a contact info.
1084 mJourneyLogger.incrementSelectionEdits( 1080 mJourneyLogger.incrementSelectionEdits(JourneyLogger.SECTION_CONTACT _INFO);
1085 PaymentRequestJourneyLogger.SECTION_CONTACT_INFO);
1086 } 1081 }
1087 mContactEditor.edit(toEdit, new Callback<AutofillContact>() { 1082 mContactEditor.edit(toEdit, new Callback<AutofillContact>() {
1088 @Override 1083 @Override
1089 public void onResult(AutofillContact editedContact) { 1084 public void onResult(AutofillContact editedContact) {
1090 if (mUI == null) return; 1085 if (mUI == null) return;
1091 1086
1092 if (editedContact != null) { 1087 if (editedContact != null) {
1093 // A partial or complete contact came back from the editor ( could have been from 1088 // A partial or complete contact came back from the editor ( could have been from
1094 // adding/editing or cancelling out of the edit flow). 1089 // adding/editing or cancelling out of the edit flow).
1095 if (!editedContact.isComplete()) { 1090 if (!editedContact.isComplete()) {
(...skipping 12 matching lines...) Expand all
1108 // action to take (if a contact was selected in the UI, it will stay selected). 1103 // action to take (if a contact was selected in the UI, it will stay selected).
1109 1104
1110 mUI.updateSection(PaymentRequestUI.TYPE_CONTACT_DETAILS, mContac tSection); 1105 mUI.updateSection(PaymentRequestUI.TYPE_CONTACT_DETAILS, mContac tSection);
1111 } 1106 }
1112 }); 1107 });
1113 } 1108 }
1114 1109
1115 private void editCard(final AutofillPaymentInstrument toEdit) { 1110 private void editCard(final AutofillPaymentInstrument toEdit) {
1116 if (toEdit != null) { 1111 if (toEdit != null) {
1117 // Log the edit of a credit card. 1112 // Log the edit of a credit card.
1118 mJourneyLogger.incrementSelectionEdits( 1113 mJourneyLogger.incrementSelectionEdits(JourneyLogger.SECTION_CREDIT_ CARDS);
1119 PaymentRequestJourneyLogger.SECTION_CREDIT_CARDS);
1120 } 1114 }
1121 mCardEditor.edit(toEdit, new Callback<AutofillPaymentInstrument>() { 1115 mCardEditor.edit(toEdit, new Callback<AutofillPaymentInstrument>() {
1122 @Override 1116 @Override
1123 public void onResult(AutofillPaymentInstrument editedCard) { 1117 public void onResult(AutofillPaymentInstrument editedCard) {
1124 if (mUI == null) return; 1118 if (mUI == null) return;
1125 1119
1126 if (editedCard != null) { 1120 if (editedCard != null) {
1127 // A partial or complete card came back from the editor (cou ld have been from 1121 // A partial or complete card came back from the editor (cou ld have been from
1128 // adding/editing or cancelling out of the edit flow). 1122 // adding/editing or cancelling out of the edit flow).
1129 if (!editedCard.isComplete()) { 1123 if (!editedCard.isComplete()) {
(...skipping 261 matching lines...) Expand 10 before | Expand all | Expand 10 after
1391 // > Non-autofill instruments. 1385 // > Non-autofill instruments.
1392 // > Complete autofill instruments. 1386 // > Complete autofill instruments.
1393 // > Incomplete autofill instruments. 1387 // > Incomplete autofill instruments.
1394 Collections.sort(mPendingAutofillInstruments, COMPLETENESS_COMPARATOR); 1388 Collections.sort(mPendingAutofillInstruments, COMPLETENESS_COMPARATOR);
1395 Collections.sort(mPendingInstruments, APP_FRECENCY_COMPARATOR); 1389 Collections.sort(mPendingInstruments, APP_FRECENCY_COMPARATOR);
1396 if (!mPendingAutofillInstruments.isEmpty()) { 1390 if (!mPendingAutofillInstruments.isEmpty()) {
1397 mPendingInstruments.add(mPendingAutofillInstruments); 1391 mPendingInstruments.add(mPendingAutofillInstruments);
1398 } 1392 }
1399 1393
1400 // Log the number of suggested credit cards. 1394 // Log the number of suggested credit cards.
1401 mJourneyLogger.setNumberOfSuggestionsShown(PaymentRequestJourneyLogger.S ECTION_CREDIT_CARDS, 1395 mJourneyLogger.setNumberOfSuggestionsShown(
1402 mPendingAutofillInstruments.size()); 1396 JourneyLogger.SECTION_CREDIT_CARDS, mPendingAutofillInstruments. size());
1403 1397
1404 // Possibly pre-select the first instrument on the list. 1398 // Possibly pre-select the first instrument on the list.
1405 int selection = SectionInformation.NO_SELECTION; 1399 int selection = SectionInformation.NO_SELECTION;
1406 if (!mPendingInstruments.isEmpty()) { 1400 if (!mPendingInstruments.isEmpty()) {
1407 PaymentInstrument first = mPendingInstruments.get(0).get(0); 1401 PaymentInstrument first = mPendingInstruments.get(0).get(0);
1408 if (first instanceof AutofillPaymentInstrument) { 1402 if (first instanceof AutofillPaymentInstrument) {
1409 AutofillPaymentInstrument creditCard = (AutofillPaymentInstrumen t) first; 1403 AutofillPaymentInstrument creditCard = (AutofillPaymentInstrumen t) first;
1410 if (creditCard.isComplete()) selection = 0; 1404 if (creditCard.isComplete()) selection = 0;
1411 } else { 1405 } else {
1412 // If a payment app is available, then PaymentRequest.canMakePay ment() returns true. 1406 // If a payment app is available, then PaymentRequest.canMakePay ment() returns true.
(...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after
1678 1672
1679 /** 1673 /**
1680 * The frecency score is calculated according to use count and last use date . The formula is 1674 * The frecency score is calculated according to use count and last use date . The formula is
1681 * the same as the one used in GetFrecencyScore in autofill_data_model.cc. 1675 * the same as the one used in GetFrecencyScore in autofill_data_model.cc.
1682 */ 1676 */
1683 private static final double getFrecencyScore(int count, long date) { 1677 private static final double getFrecencyScore(int count, long date) {
1684 long currentTime = System.currentTimeMillis(); 1678 long currentTime = System.currentTimeMillis();
1685 return -Math.log((currentTime - date) / (24 * 60 * 60 * 1000) + 2) / Mat h.log(count + 2); 1679 return -Math.log((currentTime - date) / (24 * 60 * 60 * 1000) + 2) / Mat h.log(count + 2);
1686 } 1680 }
1687 } 1681 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698