diff options
author | wangqi <wangqi@google.com> | 2018-05-21 12:29:32 -0700 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-05-21 13:18:02 -0700 |
commit | 71a22dc081e458706f07beb1684087dc4a6aedf5 (patch) | |
tree | d43592dc0b93433f7b7aa8c82888c9ad69ed8d40 /java/com/android/dialer/promotion | |
parent | 929539eb864822d669265b142bdcc49b6cf8ea6b (diff) |
Add promotion module.
Refactor Duo disclosure card to general promotion card.
Bug: 78905507
Test: unit tests
PiperOrigin-RevId: 197436677
Change-Id: I511c39308cadfb96ee4519b71ca29b75d0e6750b
Diffstat (limited to 'java/com/android/dialer/promotion')
-rw-r--r-- | java/com/android/dialer/promotion/Promotion.java | 36 | ||||
-rw-r--r-- | java/com/android/dialer/promotion/PromotionComponent.java | 43 | ||||
-rw-r--r-- | java/com/android/dialer/promotion/PromotionManager.java | 67 | ||||
-rw-r--r-- | java/com/android/dialer/promotion/impl/AndroidManifest.xml (renamed from java/com/android/dialer/promotion/AndroidManifest.xml) | 9 | ||||
-rw-r--r-- | java/com/android/dialer/promotion/impl/DuoPromotion.java | 132 | ||||
-rw-r--r-- | java/com/android/dialer/promotion/impl/PromotionModule.java | 36 | ||||
-rw-r--r-- | java/com/android/dialer/promotion/impl/RttPromotion.java (renamed from java/com/android/dialer/promotion/RttPromotion.java) | 50 | ||||
-rw-r--r-- | java/com/android/dialer/promotion/impl/res/values/strings.xml (renamed from java/com/android/dialer/promotion/res/values/strings.xml) | 9 |
8 files changed, 357 insertions, 25 deletions
diff --git a/java/com/android/dialer/promotion/Promotion.java b/java/com/android/dialer/promotion/Promotion.java index 3cd16d4a6..176606ff4 100644 --- a/java/com/android/dialer/promotion/Promotion.java +++ b/java/com/android/dialer/promotion/Promotion.java @@ -17,23 +17,51 @@ package com.android.dialer.promotion; import android.support.annotation.DrawableRes; +import android.support.annotation.IntDef; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** Interface for promotion bottom sheet. */ public interface Promotion { - /** Returns if this promotion should be shown. */ - boolean shouldShow(); + /** + * Type of promotion, which means promotion should be shown as a card in {@link + * android.support.v7.widget.RecyclerView} or {@link + * android.support.design.bottomsheet.BottomSheetBehavior}. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({PromotionType.CARD, PromotionType.BOTTOM_SHEET}) + @interface PromotionType { + /** Shown as card in call log or voicemail tab. */ + int CARD = 1; - /** Sets to show this promotion. */ - void setShouldShow(boolean shouldShow); + /** Shown as bottom sheet. */ + int BOTTOM_SHEET = 2; + } + + /** Returns {@link PromotionType} for this promotion. */ + @PromotionType + int getType(); + + /** + * Returns if this promotion should be shown. This usually means the promotion is enabled and not + * dismissed yet. + */ + boolean isEligibleToBeShown(); + + /** Called when this promotion is first time viewed by user. */ + default void onViewed() {} /** Dismisses this promotion. This is called when user acknowledged the promotion. */ void dismiss(); + /** Returns title text of the promotion. */ CharSequence getTitle(); + /** Returns details text of the promotion. */ CharSequence getDetails(); + /** Returns resource id of the icon for the promotion. */ @DrawableRes int getIconRes(); } diff --git a/java/com/android/dialer/promotion/PromotionComponent.java b/java/com/android/dialer/promotion/PromotionComponent.java new file mode 100644 index 000000000..caf10dbb0 --- /dev/null +++ b/java/com/android/dialer/promotion/PromotionComponent.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.promotion; + +import android.content.Context; +import com.android.dialer.inject.HasRootComponent; +import com.android.dialer.inject.IncludeInDialerRoot; +import com.google.common.collect.ImmutableList; +import dagger.Subcomponent; + +/** Component for promotion. */ +@Subcomponent +public abstract class PromotionComponent { + + public abstract PromotionManager promotionManager(); + + public abstract ImmutableList<Promotion> priorityPromotionList(); + + public static PromotionComponent get(Context context) { + return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component()) + .promotionComponent(); + } + + /** Used to refer to the root application component. */ + @IncludeInDialerRoot + public interface HasComponent { + PromotionComponent promotionComponent(); + } +} diff --git a/java/com/android/dialer/promotion/PromotionManager.java b/java/com/android/dialer/promotion/PromotionManager.java new file mode 100644 index 000000000..a86a745ad --- /dev/null +++ b/java/com/android/dialer/promotion/PromotionManager.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.promotion; + +import com.android.dialer.promotion.Promotion.PromotionType; +import com.google.common.collect.ImmutableList; +import java.util.Optional; +import javax.inject.Inject; + +/** + * A class to manage all promotion cards/bottom sheet. + * + * <p>Only one promotion with highest priority will be shown at a time no matter type. So if there + * are one card and one bottom sheet promotion, either one will be shown instead of both. + */ +public final class PromotionManager { + + /** Promotion priority order list. Promotions with higher priority must be added first. */ + private ImmutableList<Promotion> priorityPromotionList; + + @Inject + public PromotionManager(ImmutableList<Promotion> priorityPromotionList) { + this.priorityPromotionList = priorityPromotionList; + } + + /** + * Returns promotion should show with highest priority. {@link Optional#empty()} if no promotion + * should be shown with given {@link PromotionType}. + * + * <p>e.g. if FooPromotion(card, high priority) and BarPromotion(bottom sheet, low priority) are + * both enabled, getHighestPriorityPromotion(CARD) returns Optional.of(FooPromotion) but + * getHighestPriorityPromotion(BOTTOM_SHEET) returns {@link Optional#empty()}. + * + * <p>Currently it only supports promotion in call log tab. + * + * <p>TODO(wangqi): add support for other tabs. + */ + @SuppressWarnings("AndroidApiChecker") // Use of optional + public Optional<Promotion> getHighestPriorityPromotion(@PromotionType int type) { + for (Promotion promotion : priorityPromotionList) { + if (promotion.isEligibleToBeShown()) { + if (promotion.getType() == type) { + return Optional.of(promotion); + } else { + // Returns empty promotion since it's not the type looking for and only one promotion + // should be shown at a time. + return Optional.empty(); + } + } + } + return Optional.empty(); + } +} diff --git a/java/com/android/dialer/promotion/AndroidManifest.xml b/java/com/android/dialer/promotion/impl/AndroidManifest.xml index bd85b104f..c938b9a37 100644 --- a/java/com/android/dialer/promotion/AndroidManifest.xml +++ b/java/com/android/dialer/promotion/impl/AndroidManifest.xml @@ -13,4 +13,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<manifest package="com.android.dialer.promotion"/> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.dialer.promotion"> + + <uses-sdk + android:minSdkVersion="24" + android:targetSdkVersion="28"/> + +</manifest> diff --git a/java/com/android/dialer/promotion/impl/DuoPromotion.java b/java/com/android/dialer/promotion/impl/DuoPromotion.java new file mode 100644 index 000000000..750e4a196 --- /dev/null +++ b/java/com/android/dialer/promotion/impl/DuoPromotion.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.promotion.impl; + +import android.content.Context; +import android.content.SharedPreferences; +import android.support.annotation.VisibleForTesting; +import com.android.dialer.configprovider.ConfigProvider; +import com.android.dialer.duo.Duo; +import com.android.dialer.inject.ApplicationContext; +import com.android.dialer.promotion.Promotion; +import com.android.dialer.spannable.ContentWithLearnMoreSpanner; +import com.android.dialer.storage.Unencrypted; +import java.util.concurrent.TimeUnit; +import javax.inject.Inject; + +/** Duo promotion. */ +final class DuoPromotion implements Promotion { + private static final String SHARED_PREF_KEY_DUO_DISCLOSURE_DISMISSED = "duo_disclosure_dismissed"; + + private static final String SHARED_PREF_KEY_DUO_DISCLOSURE_FIRST_VIEW_TIME_MILLIS = + "duo_disclosure_first_viewed_time_ms"; + @VisibleForTesting static final String FLAG_SHOW_DUO_DISCLOSURE = "show_duo_disclosure"; + + @VisibleForTesting + static final String FLAG_DUO_DISCLOSURE_AUTO_DISMISS_AFTER_VIEWED_TIME_MILLIS = + "show_duo_disclosure_auto_dismiss_after_viewed_time_millis"; + + private final Context appContext; + private final ConfigProvider configProvider; + private final Duo duo; + private final SharedPreferences sharedPreferences; + + @Inject + DuoPromotion( + @ApplicationContext Context context, + ConfigProvider configProvider, + Duo duo, + @Unencrypted SharedPreferences sharedPreferences) { + this.appContext = context; + this.configProvider = configProvider; + this.duo = duo; + this.sharedPreferences = sharedPreferences; + } + + @Override + public int getType() { + return PromotionType.CARD; + } + + @Override + public boolean isEligibleToBeShown() { + if (!configProvider.getBoolean(FLAG_SHOW_DUO_DISCLOSURE, false)) { + return false; + } + // Don't show the Duo disclosure card if + // (1) Duo integration is not enabled on the device, or + // (2) Duo is not activated. + if (!duo.isEnabled(appContext) || !duo.isActivated(appContext)) { + return false; + } + + // Don't show the Duo disclosure card if it has been dismissed. + if (sharedPreferences.getBoolean(SHARED_PREF_KEY_DUO_DISCLOSURE_DISMISSED, false)) { + return false; + } + + // At this point, Duo is activated and the disclosure card hasn't been dismissed. + // We should show the card if it has never been viewed by the user. + if (!sharedPreferences.contains(SHARED_PREF_KEY_DUO_DISCLOSURE_FIRST_VIEW_TIME_MILLIS)) { + return true; + } + + // At this point, the card has been viewed but not dismissed. + // We should not show the card if it has been viewed for more than 1 day. + long duoDisclosureFirstViewTimeMillis = + sharedPreferences.getLong(SHARED_PREF_KEY_DUO_DISCLOSURE_FIRST_VIEW_TIME_MILLIS, 0); + return System.currentTimeMillis() - duoDisclosureFirstViewTimeMillis + <= configProvider.getLong( + FLAG_DUO_DISCLOSURE_AUTO_DISMISS_AFTER_VIEWED_TIME_MILLIS, TimeUnit.DAYS.toMillis(1)); + } + + @Override + public void onViewed() { + if (!sharedPreferences.contains(SHARED_PREF_KEY_DUO_DISCLOSURE_FIRST_VIEW_TIME_MILLIS)) { + sharedPreferences + .edit() + .putLong( + SHARED_PREF_KEY_DUO_DISCLOSURE_FIRST_VIEW_TIME_MILLIS, System.currentTimeMillis()) + .apply(); + } + } + + @Override + public void dismiss() { + sharedPreferences.edit().putBoolean(SHARED_PREF_KEY_DUO_DISCLOSURE_DISMISSED, true).apply(); + } + + @Override + public CharSequence getTitle() { + return appContext.getString(R.string.duo_disclosure_title); + } + + @Override + public CharSequence getDetails() { + return new ContentWithLearnMoreSpanner(appContext) + .create( + appContext.getString(R.string.duo_disclosure_details), + configProvider.getString( + "duo_disclosure_link_full_url", + "http://support.google.com/pixelphone/?p=dialer_duo")); + } + + @Override + public int getIconRes() { + return duo.getLogo(); + } +} diff --git a/java/com/android/dialer/promotion/impl/PromotionModule.java b/java/com/android/dialer/promotion/impl/PromotionModule.java new file mode 100644 index 000000000..f0908780c --- /dev/null +++ b/java/com/android/dialer/promotion/impl/PromotionModule.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.dialer.promotion.impl; + +import com.android.dialer.inject.DialerVariant; +import com.android.dialer.inject.InstallIn; +import com.android.dialer.promotion.Promotion; +import com.google.common.collect.ImmutableList; +import dagger.Module; +import dagger.Provides; + +/** Module for Promotion. */ +@InstallIn(variants = {DialerVariant.DIALER_TEST}) +@Module +public abstract class PromotionModule { + + @Provides + static ImmutableList<Promotion> providePriorityPromotionList( + RttPromotion rttPromotion, DuoPromotion duoPromotion) { + return ImmutableList.of(rttPromotion, duoPromotion); + } +} diff --git a/java/com/android/dialer/promotion/RttPromotion.java b/java/com/android/dialer/promotion/impl/RttPromotion.java index feb6e4734..f0f7f546b 100644 --- a/java/com/android/dialer/promotion/RttPromotion.java +++ b/java/com/android/dialer/promotion/impl/RttPromotion.java @@ -14,29 +14,45 @@ * limitations under the License */ -package com.android.dialer.promotion; +package com.android.dialer.promotion.impl; import android.content.Context; import android.content.SharedPreferences; import android.support.annotation.DrawableRes; import com.android.dialer.common.LogUtil; -import com.android.dialer.configprovider.ConfigProviderBindings; +import com.android.dialer.configprovider.ConfigProvider; +import com.android.dialer.inject.ApplicationContext; +import com.android.dialer.promotion.Promotion; import com.android.dialer.spannable.ContentWithLearnMoreSpanner; import com.android.dialer.storage.StorageComponent; +import com.android.dialer.storage.Unencrypted; +import javax.inject.Inject; /** RTT promotion. */ public final class RttPromotion implements Promotion { private static final String SHARED_PREFERENCE_KEY_ENABLED = "rtt_promotion_enabled"; private static final String SHARED_PREFERENCE_KEY_DISMISSED = "rtt_promotion_dismissed"; private final Context appContext; + private final SharedPreferences sharedPreferences; + private final ConfigProvider configProvider; - public RttPromotion(Context context) { - appContext = context.getApplicationContext(); + @Override + public int getType() { + return PromotionType.BOTTOM_SHEET; + } + + @Inject + RttPromotion( + @ApplicationContext Context context, + @Unencrypted SharedPreferences sharedPreferences, + ConfigProvider configProvider) { + appContext = context; + this.sharedPreferences = sharedPreferences; + this.configProvider = configProvider; } @Override - public boolean shouldShow() { - SharedPreferences sharedPreferences = StorageComponent.get(appContext).unencryptedSharedPrefs(); + public boolean isEligibleToBeShown() { return sharedPreferences.getBoolean(SHARED_PREFERENCE_KEY_ENABLED, false) && !sharedPreferences.getBoolean(SHARED_PREFERENCE_KEY_DISMISSED, false); } @@ -51,10 +67,9 @@ public final class RttPromotion implements Promotion { return new ContentWithLearnMoreSpanner(appContext) .create( appContext.getString(R.string.rtt_promotion_details), - ConfigProviderBindings.get(appContext) - .getString( - "rtt_promo_learn_more_link_full_url", - "http://support.google.com/pixelphone/?p=dialer_rtt")); + configProvider.getString( + "rtt_promo_learn_more_link_full_url", + "http://support.google.com/pixelphone/?p=dialer_rtt")); } @Override @@ -63,22 +78,17 @@ public final class RttPromotion implements Promotion { return R.drawable.quantum_ic_rtt_vd_theme_24; } - @Override - public void setShouldShow(boolean shouldShow) { - LogUtil.i("RttPromotion.setShouldShow", "shouldShow: %b", shouldShow); - StorageComponent.get(appContext) + public static void setEnabled(Context context) { + LogUtil.enterBlock("RttPromotion.setEnabled"); + StorageComponent.get(context) .unencryptedSharedPrefs() .edit() - .putBoolean(SHARED_PREFERENCE_KEY_ENABLED, shouldShow) + .putBoolean(SHARED_PREFERENCE_KEY_ENABLED, true) .apply(); } @Override public void dismiss() { - StorageComponent.get(appContext) - .unencryptedSharedPrefs() - .edit() - .putBoolean(SHARED_PREFERENCE_KEY_DISMISSED, true) - .apply(); + sharedPreferences.edit().putBoolean(SHARED_PREFERENCE_KEY_DISMISSED, true).apply(); } } diff --git a/java/com/android/dialer/promotion/res/values/strings.xml b/java/com/android/dialer/promotion/impl/res/values/strings.xml index 633671546..899074932 100644 --- a/java/com/android/dialer/promotion/res/values/strings.xml +++ b/java/com/android/dialer/promotion/impl/res/values/strings.xml @@ -23,4 +23,13 @@ disability, or need more than voice alone. RTT messaging transcripts are stored on your device in the call history. <xliff:g exmaple="Learn More">%1$s</xliff:g></string> + <!-- Header on the Duo disclosure card. [CHAR_LIMIT=60] --> + <string name="duo_disclosure_title"> + Make video calls with Duo + </string> + + <!-- Details on the Duo disclosure card. [CHAR_LIMIT=200] --> + <string name="duo_disclosure_details"> + Google Duo video calling lets you chat with friends and family face-to-face. Data charges may apply. <xliff:g example="Learn More">%1$s</xliff:g> + </string> </resources> |