Skip to content

Commit 962d749

Browse files
authored
Support Android 13 and request notification permission (#535)
1 parent 466a74b commit 962d749

File tree

15 files changed

+470
-26
lines changed

15 files changed

+470
-26
lines changed

AndroidSDKCore/src/main/java/com/leanplum/messagetemplates/MessageTemplateConstants.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ public static class Args {
1515
public static final String CANCEL_ACTION = "Cancel action";
1616
public static final String DISMISS_ACTION = "Dismiss action";
1717

18-
// Center popup/interstitial arguments.
18+
// Center popup / interstitial / Push Pre-Permission arguments.
1919
public static final String TITLE_TEXT = "Title.Text";
2020
public static final String TITLE_COLOR = "Title.Color";
2121
public static final String MESSAGE_TEXT = "Message.Text";
2222
public static final String MESSAGE_COLOR = "Message.Color";
2323
public static final String ACCEPT_BUTTON_TEXT = "Accept button.Text";
2424
public static final String ACCEPT_BUTTON_BACKGROUND_COLOR = "Accept button.Background color";
2525
public static final String ACCEPT_BUTTON_TEXT_COLOR = "Accept button.Text color";
26+
public static final String CANCEL_BUTTON_TEXT = "Cancel button.Text";
27+
public static final String CANCEL_BUTTON_BACKGROUND_COLOR = "Cancel button.Background color";
28+
public static final String CANCEL_BUTTON_TEXT_COLOR = "Cancel button.Text color";
2629
public static final String BACKGROUND_IMAGE = "Background image";
2730
public static final String BACKGROUND_COLOR = "Background color";
2831
public static final String LAYOUT_WIDTH = "Layout.Width";
@@ -51,9 +54,11 @@ public static class Values {
5154
public static final String CONFIRM_MESSAGE = "Confirmation message goes here.";
5255
public static final String POPUP_MESSAGE = "Popup message goes here.";
5356
public static final String INTERSTITIAL_MESSAGE = "Interstitial message goes here.";
57+
public static final String PUSH_PRE_PERMISSION_MESSAGE = "Tap OK to receive important notifications from our app.";
5458
public static final String OK_TEXT = "OK";
5559
public static final String YES_TEXT = "Yes";
5660
public static final String NO_TEXT = "No";
61+
public static final String MAYBE_LATER = "Maybe Later";
5762
public static final int CENTER_POPUP_WIDTH = 300;
5863
public static final int CENTER_POPUP_HEIGHT = 250;
5964
public static final int DEFAULT_HTML_HEIGHT = 0;

AndroidSDKCore/src/main/java/com/leanplum/messagetemplates/MessageTemplates.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import com.leanplum.messagetemplates.actions.AlertMessage;
3535
import com.leanplum.messagetemplates.actions.CenterPopupMessage;
3636
import com.leanplum.messagetemplates.actions.InterstitialMessage;
37+
import com.leanplum.messagetemplates.actions.PushPrePermission;
38+
import com.leanplum.messagetemplates.actions.RegisterForPush;
3739
import com.leanplum.messagetemplates.actions.RichHtmlMessage;
3840
import com.leanplum.messagetemplates.actions.WebInterstitialMessage;
3941
import com.leanplum.messagetemplates.actions.ConfirmMessage;
@@ -136,5 +138,7 @@ public synchronized static void register(Context context) {
136138
registerTemplate(new InterstitialMessage(), context);
137139
registerTemplate(new WebInterstitialMessage(), context);
138140
registerTemplate(new RichHtmlMessage(), context);
141+
registerAction(new RegisterForPush(), context);
142+
registerTemplate(new PushPrePermission(), context);
139143
}
140144
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2023, Leanplum, Inc. All rights reserved.
3+
*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
package com.leanplum.messagetemplates.actions;
23+
24+
import android.app.Activity;
25+
import android.content.Context;
26+
import androidx.annotation.NonNull;
27+
import com.leanplum.ActionArgs;
28+
import com.leanplum.ActionContext;
29+
import com.leanplum.LeanplumActivityHelper;
30+
import com.leanplum.internal.Log;
31+
import com.leanplum.internal.OperationQueue;
32+
import com.leanplum.messagetemplates.MessageTemplate;
33+
import com.leanplum.messagetemplates.controllers.CenterPopupController;
34+
import com.leanplum.messagetemplates.options.PushPrePermissionOptions;
35+
import com.leanplum.utils.PushPermissionUtilKt;
36+
37+
public class PushPrePermission implements MessageTemplate {
38+
private static final String PUSH_ASK_TO_ASK = "Push Ask to Ask";
39+
40+
private CenterPopupController popup;
41+
42+
@NonNull
43+
@Override
44+
public String getName() {
45+
return PUSH_ASK_TO_ASK;
46+
}
47+
48+
@NonNull
49+
@Override
50+
public ActionArgs createActionArgs(@NonNull Context context) {
51+
return PushPrePermissionOptions.toArgs(context);
52+
}
53+
54+
@Override
55+
public boolean present(@NonNull ActionContext context) {
56+
Activity activity = LeanplumActivityHelper.getCurrentActivity();
57+
if (activity == null || activity.isFinishing()) {
58+
return false;
59+
}
60+
61+
if (PushPermissionUtilKt.shouldShowPrePermission(activity)) {
62+
showPrePermission(context, activity);
63+
return true;
64+
} else if (PushPermissionUtilKt.shouldShowRegisterForPush(activity)) {
65+
showRegisterForPush(context, activity);
66+
return true;
67+
} else {
68+
// nothing to do
69+
Log.d("Will not show Push Pre-Permission dialog because:");
70+
PushPermissionUtilKt.printDebugLog(activity);
71+
return false;
72+
}
73+
}
74+
75+
private void showPrePermission(ActionContext context, Activity activity) {
76+
PushPrePermissionOptions options = new PushPrePermissionOptions(context);
77+
popup = new CenterPopupController(activity, options);
78+
popup.setOnDismissListener(listener -> {
79+
popup = null;
80+
context.actionDismissed();
81+
});
82+
popup.show();
83+
}
84+
85+
private void showRegisterForPush(ActionContext context, Activity activity) {
86+
// Run after ActionManagerExecution code
87+
OperationQueue.sharedInstance().addUiOperation(() -> {
88+
context.actionDismissed();
89+
// Flow of permission request will be shown on top of any other in-app
90+
PushPermissionUtilKt.requestNativePermission(activity);
91+
});
92+
}
93+
94+
@Override
95+
public boolean dismiss(@NonNull ActionContext context) {
96+
if (popup != null) {
97+
popup.dismiss();
98+
}
99+
return true;
100+
}
101+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2023, Leanplum, Inc. All rights reserved.
3+
*
4+
* Licensed to the Apache Software Foundation (ASF) under one
5+
* or more contributor license agreements. See the NOTICE file
6+
* distributed with this work for additional information
7+
* regarding copyright ownership. The ASF licenses this file
8+
* to you under the Apache License, Version 2.0 (the
9+
* "License"); you may not use this file except in compliance
10+
* with the License. You may obtain a copy of the License at
11+
*
12+
* http://www.apache.org/licenses/LICENSE-2.0
13+
*
14+
* Unless required by applicable law or agreed to in writing,
15+
* software distributed under the License is distributed on an
16+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
* KIND, either express or implied. See the License for the
18+
* specific language governing permissions and limitations
19+
* under the License.
20+
*/
21+
22+
package com.leanplum.messagetemplates.actions;
23+
24+
import android.app.Activity;
25+
import android.content.Context;
26+
import androidx.annotation.NonNull;
27+
import com.leanplum.ActionArgs;
28+
import com.leanplum.ActionContext;
29+
import com.leanplum.LeanplumActivityHelper;
30+
import com.leanplum.internal.Log;
31+
import com.leanplum.internal.OperationQueue;
32+
import com.leanplum.messagetemplates.MessageTemplate;
33+
import com.leanplum.utils.PushPermissionUtilKt;
34+
35+
public class RegisterForPush implements MessageTemplate {
36+
private static final String REGISTER_FOR_PUSH = "Register For Push";
37+
38+
@NonNull
39+
@Override
40+
public String getName() {
41+
return REGISTER_FOR_PUSH;
42+
}
43+
44+
@NonNull
45+
@Override
46+
public ActionArgs createActionArgs(@NonNull Context context) {
47+
return new ActionArgs();
48+
}
49+
50+
@Override
51+
public boolean present(@NonNull ActionContext context) {
52+
Activity activity = LeanplumActivityHelper.getCurrentActivity();
53+
if (activity == null || activity.isFinishing()) {
54+
return false;
55+
}
56+
57+
if (PushPermissionUtilKt.shouldShowRegisterForPush(activity)) {
58+
showRegisterForPush(context, activity);
59+
return true;
60+
} else {
61+
Log.d("Will not show Register For Push dialog because:");
62+
PushPermissionUtilKt.printDebugLog(activity);
63+
return false;
64+
}
65+
}
66+
67+
private void showRegisterForPush(ActionContext context, Activity activity) {
68+
// Run after the other ActionManagerExecution code
69+
OperationQueue.sharedInstance().addUiOperation(() -> {
70+
context.actionDismissed();
71+
// Flow of permission request will be shown on top of any other in-app
72+
PushPermissionUtilKt.requestNativePermission(activity);
73+
});
74+
}
75+
76+
@Override
77+
public boolean dismiss(@NonNull ActionContext context) {
78+
// nothing to do
79+
return true;
80+
}
81+
}

AndroidSDKCore/src/main/java/com/leanplum/messagetemplates/controllers/AbstractPopupController.java

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import android.view.View;
3030
import android.view.ViewGroup.LayoutParams;
3131
import android.widget.ImageView;
32+
import android.widget.LinearLayout;
3233
import android.widget.RelativeLayout;
3334
import android.widget.TextView;
3435
import androidx.annotation.VisibleForTesting;
@@ -48,15 +49,11 @@ abstract class AbstractPopupController extends BaseController {
4849
protected AbstractPopupController(Activity activity, BaseMessageOptions options) {
4950
super(activity);
5051
this.options = options;
52+
this.hasDismissButton = options.hasDismissButton();
5153

5254
init();
5355
}
5456

55-
@Override
56-
protected boolean hasDismissButton() {
57-
return true;
58-
}
59-
6057
@VisibleForTesting
6158
public BaseMessageOptions getOptions() {
6259
return options;
@@ -75,14 +72,28 @@ void addMessageChildViews(RelativeLayout parent) {
7572
View title = createTitleView(activity);
7673
parent.addView(title);
7774

78-
View button = createAcceptButton(activity);
79-
parent.addView(button);
75+
TextView acceptButton;
76+
View buttonContainer = null;
77+
if (options.hasCancelButtonNextToAccept()) {
78+
acceptButton = createAcceptButton(activity);
79+
TextView cancelButton = createCancelButton(activity);
80+
buttonContainer = createButtonContainer(activity, cancelButton, acceptButton);
81+
parent.addView(buttonContainer);
82+
} else {
83+
acceptButton = createAcceptButton(activity);
84+
parent.addView(acceptButton);
85+
}
8086

8187
View message = createMessageView(activity);
8288
((RelativeLayout.LayoutParams) message.getLayoutParams())
8389
.addRule(RelativeLayout.BELOW, title.getId());
84-
((RelativeLayout.LayoutParams) message.getLayoutParams())
85-
.addRule(RelativeLayout.ABOVE, button.getId());
90+
if (options.hasCancelButtonNextToAccept()) {
91+
((RelativeLayout.LayoutParams) message.getLayoutParams())
92+
.addRule(RelativeLayout.ABOVE, buttonContainer.getId());
93+
} else {
94+
((RelativeLayout.LayoutParams) message.getLayoutParams())
95+
.addRule(RelativeLayout.ABOVE, acceptButton.getId());
96+
}
8697
parent.addView(message);
8798
}
8899

@@ -123,6 +134,31 @@ private RelativeLayout createTitleView(Context context) {
123134
return view;
124135
}
125136

137+
private LinearLayout createButtonContainer(Context context, TextView buttonLeft, TextView buttonRight) {
138+
LinearLayout ll = new LinearLayout(context);
139+
ll.setId(R.id.button_container);
140+
ll.setOrientation(LinearLayout.HORIZONTAL);
141+
ll.setWeightSum(1f);
142+
RelativeLayout.LayoutParams layoutParams =
143+
new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
144+
layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
145+
ll.setLayoutParams(layoutParams);
146+
147+
LinearLayout.LayoutParams lpLeft =
148+
new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 0.5f);
149+
buttonLeft.setLayoutParams(lpLeft);
150+
buttonLeft.setGravity(Gravity.CENTER_HORIZONTAL);
151+
152+
LinearLayout.LayoutParams lpRight =
153+
new LinearLayout.LayoutParams(0, LayoutParams.WRAP_CONTENT, 0.5f);
154+
buttonRight.setLayoutParams(lpRight);
155+
buttonRight.setGravity(Gravity.CENTER_HORIZONTAL);
156+
157+
ll.addView(buttonLeft);
158+
ll.addView(buttonRight);
159+
return ll;
160+
}
161+
126162
private TextView createAcceptButton(Context context) {
127163
TextView view = new TextView(context);
128164
view.setId(R.id.accept_button);
@@ -151,6 +187,28 @@ private TextView createAcceptButton(Context context) {
151187
return view;
152188
}
153189

190+
private TextView createCancelButton(Context context) {
191+
TextView view = new TextView(context);
192+
view.setId(R.id.cancel_button);
193+
194+
view.setPadding(SizeUtil.dp20, SizeUtil.dp5, SizeUtil.dp20, SizeUtil.dp5);
195+
view.setText(options.getCancelButtonText());
196+
view.setTextColor(options.getCancelButtonTextColor());
197+
view.setTypeface(null, Typeface.BOLD);
198+
199+
BitmapUtil.stateBackgroundDarkerByPercentage(view,
200+
options.getCancelButtonBgColor(), 30);
201+
202+
view.setTextSize(TypedValue.COMPLEX_UNIT_SP, SizeUtil.textSize0_1);
203+
view.setOnClickListener(clickedView -> {
204+
if (!isClosing) {
205+
options.cancel();
206+
cancel();
207+
}
208+
});
209+
return view;
210+
}
211+
154212
private TextView createMessageView(Context context) {
155213
TextView view = new TextView(context);
156214
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(

AndroidSDKCore/src/main/java/com/leanplum/messagetemplates/controllers/BaseController.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ abstract class BaseController extends Dialog {
5050

5151
protected boolean isClosing = false;
5252

53+
protected boolean hasDismissButton = false;
54+
5355
protected BaseController(Activity activity) {
5456
super(activity, ViewUtils.getThemeId(activity));
5557
this.activity = activity;
@@ -62,7 +64,7 @@ protected void init() {
6264
RelativeLayout messageView = createMessageView();
6365
contentView.addView(messageView);
6466

65-
if (hasDismissButton()) {
67+
if (hasDismissButton) {
6668
CloseButton closeButton = createCloseButton(messageView);
6769
contentView.addView(closeButton);
6870
}
@@ -108,8 +110,6 @@ private RelativeLayout createMessageView() {
108110
*/
109111
abstract void addMessageChildViews(RelativeLayout parent);
110112

111-
abstract boolean hasDismissButton();
112-
113113
abstract boolean isFullscreen();
114114

115115
/**

AndroidSDKCore/src/main/java/com/leanplum/messagetemplates/controllers/RichHtmlController.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,6 @@ public RichHtmlController(Activity activity, @NonNull RichHtmlOptions richOption
7070
init();
7171
}
7272

73-
@Override
74-
boolean hasDismissButton() {
75-
return false;
76-
}
77-
7873
@Override
7974
boolean isFullscreen() {
8075
return richOptions.isFullScreen();
@@ -332,7 +327,7 @@ private void initWebSettings(WebView webView) {
332327
if (Build.VERSION.SDK_INT >= 17) {
333328
webViewSettings.setMediaPlaybackRequiresUserGesture(false);
334329
}
335-
webViewSettings.setAppCacheEnabled(true);
330+
webViewSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
336331
webViewSettings.setAllowFileAccess(true);
337332
webViewSettings.setJavaScriptEnabled(true);
338333
webViewSettings.setDomStorageEnabled(true);

0 commit comments

Comments
 (0)