Skip to content

Commit 1fccbb5

Browse files
Merge branch 'staging' into preview_content
2 parents 959e011 + 6c7d8da commit 1fccbb5

43 files changed

Lines changed: 2921 additions & 665 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## XX.XX.XX
22
* Added Content feature method `previewContent(String contentId)` (Experimental!).
3+
* Improved content display positioning in safe area mode
4+
* Improved Content refresh mechanics.
35

46
## 26.1.0
57
* Extended server configuration capabilities with server-controlled listing filters:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/b26d1acc435c47af88b4e4b9eb94f59f)](https://app.codacy.com/gh/Countly/countly-sdk-android/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
2-
[![API](https://img.shields.io/badge/API-9%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=9)
2+
![API](https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat)
33

44
# Countly Android SDK
55

app-kotlin/build.gradle

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,7 @@ android {
2525
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
2626
}
2727
}
28-
compileOptions {
29-
sourceCompatibility JavaVersion.VERSION_1_8
30-
targetCompatibility JavaVersion.VERSION_1_8
31-
}
32-
33-
kotlinOptions {
34-
jvmTarget = '1.8'
35-
}
28+
3629
dataBinding {
3730
enabled = true
3831
}

app/build.gradle

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,19 @@ repositories {
2828
}
2929
}
3030

31+
kotlin {
32+
jvmToolchain(17)
33+
}
34+
3135
android {
3236
compileSdk 35
3337
namespace 'ly.count.android.demo'
3438

39+
compileOptions {
40+
sourceCompatibility JavaVersion.VERSION_17
41+
targetCompatibility JavaVersion.VERSION_17
42+
}
43+
3544
signingConfigs {
3645
release {
3746
storeFile file('keys')

app/src/main/AndroidManifest.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<activity
2121
android:name=".MainActivity"
2222
android:label="@string/app_name"
23+
android:theme="@style/AppTheme.NoActionBar"
2324
android:configChanges="orientation|screenSize"
2425
android:exported="true">
2526
<intent-filter>
@@ -105,6 +106,11 @@
105106
android:label="@string/activity_name_content_zone"
106107
android:configChanges="orientation|screenSize"/>
107108

109+
<activity
110+
android:name=".ActivityExampleFragments"
111+
android:label="Fragment Navigation Test"
112+
android:configChanges="orientation|screenSize"/>
113+
108114
<activity
109115
android:name=".ActivityExampleTests"
110116
android:exported="false"/>
@@ -149,6 +155,21 @@
149155
</intent-filter>
150156
</activity>
151157
<activity android:name=".ActivityExampleKotlin"/>
158+
159+
<activity
160+
android:name=".ActivityExampleConsent"
161+
android:label="@string/activity_name_consent"
162+
android:configChanges="orientation|screenSize"/>
163+
164+
<activity
165+
android:name=".ActivityExampleLocation"
166+
android:label="@string/activity_name_location"
167+
android:configChanges="orientation|screenSize"/>
168+
169+
<activity
170+
android:name=".ActivityExampleSessions"
171+
android:label="@string/activity_name_sessions"
172+
android:configChanges="orientation|screenSize"/>
152173
</application>
153174

154175
</manifest>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package ly.count.android.demo;
2+
3+
import android.os.Bundle;
4+
import android.widget.Toast;
5+
6+
import androidx.appcompat.app.AppCompatActivity;
7+
8+
import com.google.android.material.switchmaterial.SwitchMaterial;
9+
10+
import ly.count.android.sdk.Countly;
11+
12+
public class ActivityExampleConsent extends AppCompatActivity {
13+
14+
private static final String[][] FEATURES = {
15+
{"sessions", "switchSessions"},
16+
{"events", "switchEvents"},
17+
{"views", "switchViews"},
18+
{"crashes", "switchCrashes"},
19+
{"attribution", "switchAttribution"},
20+
{"users", "switchUsers"},
21+
{"push", "switchPush"},
22+
{"starRating", "switchStarRating"},
23+
{"remoteConfig", "switchRemoteConfig"},
24+
{"location", "switchLocation"},
25+
{"feedback", "switchFeedback"},
26+
{"apm", "switchApm"},
27+
{"content", "switchContent"},
28+
};
29+
30+
@Override
31+
public void onCreate(Bundle savedInstanceState) {
32+
super.onCreate(savedInstanceState);
33+
setContentView(R.layout.activity_example_consent);
34+
35+
// Set initial switch states and listeners
36+
for (String[] feature : FEATURES) {
37+
String featureName = feature[0];
38+
int resId = getResources().getIdentifier(feature[1], "id", getPackageName());
39+
SwitchMaterial sw = findViewById(resId);
40+
if (sw != null) {
41+
sw.setChecked(Countly.sharedInstance().consent().getConsent(featureName));
42+
sw.setOnCheckedChangeListener((buttonView, isChecked) -> {
43+
if (isChecked) {
44+
Countly.sharedInstance().consent().giveConsent(new String[]{featureName});
45+
} else {
46+
Countly.sharedInstance().consent().removeConsent(new String[]{featureName});
47+
}
48+
});
49+
}
50+
}
51+
52+
findViewById(R.id.btnGiveAllConsent).setOnClickListener(v -> {
53+
Countly.sharedInstance().consent().giveConsentAll();
54+
refreshSwitches();
55+
Toast.makeText(this, "All consent given", Toast.LENGTH_SHORT).show();
56+
});
57+
58+
findViewById(R.id.btnRemoveAllConsent).setOnClickListener(v -> {
59+
Countly.sharedInstance().consent().removeConsentAll();
60+
refreshSwitches();
61+
Toast.makeText(this, "All consent removed", Toast.LENGTH_SHORT).show();
62+
});
63+
}
64+
65+
private void refreshSwitches() {
66+
for (String[] feature : FEATURES) {
67+
int resId = getResources().getIdentifier(feature[1], "id", getPackageName());
68+
SwitchMaterial sw = findViewById(resId);
69+
if (sw != null) {
70+
sw.setChecked(Countly.sharedInstance().consent().getConsent(feature[0]));
71+
}
72+
}
73+
}
74+
}

app/src/main/java/ly/count/android/demo/ActivityExampleContentZone.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@ public void onClickChangeDeviceIdContentZone(View v) {
3838
String newDeviceId = deviceId.isEmpty() ? UUID.randomUUID().toString() : deviceId;
3939

4040
Countly.sharedInstance().deviceId().setID(newDeviceId);
41+
Countly.sharedInstance().consent().giveConsentAll();
4142
}
4243
}
Lines changed: 132 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,165 @@
11
package ly.count.android.demo;
22

33
import android.os.Bundle;
4+
import android.view.LayoutInflater;
45
import android.view.View;
6+
import android.widget.EditText;
7+
import android.widget.LinearLayout;
8+
import android.widget.Toast;
9+
510
import androidx.appcompat.app.AppCompatActivity;
11+
12+
import com.google.android.material.button.MaterialButton;
13+
import com.google.android.material.textfield.TextInputEditText;
14+
15+
import java.util.HashMap;
616
import java.util.Map;
7-
import java.util.Random;
8-
import java.util.concurrent.ConcurrentHashMap;
17+
918
import ly.count.android.sdk.Countly;
1019

11-
@SuppressWarnings("UnusedParameters")
1220
public class ActivityExampleCustomEvents extends AppCompatActivity {
1321

22+
private TextInputEditText inputEventName, inputEventCount, inputEventSum, inputEventDuration;
23+
private TextInputEditText inputTimedEventName;
24+
private LinearLayout segmentationContainer;
25+
1426
@Override
1527
public void onCreate(Bundle savedInstanceState) {
1628
super.onCreate(savedInstanceState);
1729
setContentView(R.layout.activity_example_custom_events);
18-
}
1930

20-
public void onClickRecordEvent01(View v) {
21-
Countly.sharedInstance().events().recordEvent("Custom event 1");
31+
inputEventName = findViewById(R.id.inputEventName);
32+
inputEventCount = findViewById(R.id.inputEventCount);
33+
inputEventSum = findViewById(R.id.inputEventSum);
34+
inputEventDuration = findViewById(R.id.inputEventDuration);
35+
inputTimedEventName = findViewById(R.id.inputTimedEventName);
36+
segmentationContainer = findViewById(R.id.segmentationContainer);
37+
38+
findViewById(R.id.btnAddSegment).setOnClickListener(v -> addSegmentRow());
39+
findViewById(R.id.btnRecordEvent).setOnClickListener(v -> recordCustomEvent());
40+
findViewById(R.id.btnStartTimedEvent).setOnClickListener(v -> startTimedEvent());
41+
findViewById(R.id.btnEndTimedEvent).setOnClickListener(v -> endTimedEvent());
42+
findViewById(R.id.btnCancelTimedEvent).setOnClickListener(v -> cancelTimedEvent());
43+
findViewById(R.id.btnPreset1).setOnClickListener(v -> recordPresetWithSegmentation());
44+
findViewById(R.id.btnPreset2).setOnClickListener(v -> recordPresetWithCountSum());
45+
findViewById(R.id.btnTriggerSend).setOnClickListener(v -> {
46+
Countly.sharedInstance().requestQueue().attemptToSendStoredRequests();
47+
Toast.makeText(this, "Sending stored requests", Toast.LENGTH_SHORT).show();
48+
});
49+
50+
// Add one default segment row
51+
addSegmentRow();
2252
}
2353

24-
public void onClickRecordEvent02(View v) {
25-
Countly.sharedInstance().events().recordEvent("Custom event 2", 3);
54+
private void addSegmentRow() {
55+
View row = LayoutInflater.from(this).inflate(R.layout.row_segmentation, segmentationContainer, false);
56+
row.findViewById(R.id.btnRemoveSegment).setOnClickListener(v -> segmentationContainer.removeView(row));
57+
segmentationContainer.addView(row);
2658
}
2759

28-
public void onClickRecordEvent03(View v) {
29-
Countly.sharedInstance().events().recordEvent("Custom event 3", 1, 134);
60+
private Map<String, Object> collectSegmentation() {
61+
Map<String, Object> segmentation = new HashMap<>();
62+
for (int i = 0; i < segmentationContainer.getChildCount(); i++) {
63+
View row = segmentationContainer.getChildAt(i);
64+
EditText keyField = row.findViewById(R.id.inputSegKey);
65+
EditText valueField = row.findViewById(R.id.inputSegValue);
66+
String key = keyField.getText().toString().trim();
67+
String value = valueField.getText().toString().trim();
68+
if (!key.isEmpty() && !value.isEmpty()) {
69+
// Try to parse as number
70+
try {
71+
if (value.contains(".")) {
72+
segmentation.put(key, Double.parseDouble(value));
73+
} else {
74+
segmentation.put(key, Integer.parseInt(value));
75+
}
76+
} catch (NumberFormatException e) {
77+
segmentation.put(key, value);
78+
}
79+
}
80+
}
81+
return segmentation;
3082
}
3183

32-
public void onClickRecordEvent04(View v) {
33-
Countly.sharedInstance().events().recordEvent("Custom event 4", null, 1, 0, 55);
84+
private void recordCustomEvent() {
85+
String eventName = inputEventName.getText() != null ? inputEventName.getText().toString().trim() : "";
86+
if (eventName.isEmpty()) {
87+
inputEventName.setError("Event name is required");
88+
return;
89+
}
90+
91+
int count = 1;
92+
double sum = 0;
93+
double duration = 0;
94+
95+
try {
96+
String countStr = inputEventCount.getText() != null ? inputEventCount.getText().toString().trim() : "";
97+
if (!countStr.isEmpty()) count = Integer.parseInt(countStr);
98+
} catch (NumberFormatException ignored) {}
99+
100+
try {
101+
String sumStr = inputEventSum.getText() != null ? inputEventSum.getText().toString().trim() : "";
102+
if (!sumStr.isEmpty()) sum = Double.parseDouble(sumStr);
103+
} catch (NumberFormatException ignored) {}
104+
105+
try {
106+
String durStr = inputEventDuration.getText() != null ? inputEventDuration.getText().toString().trim() : "";
107+
if (!durStr.isEmpty()) duration = Double.parseDouble(durStr);
108+
} catch (NumberFormatException ignored) {}
109+
110+
Map<String, Object> segmentation = collectSegmentation();
111+
112+
if (segmentation.isEmpty()) {
113+
Countly.sharedInstance().events().recordEvent(eventName, count, sum);
114+
} else {
115+
Countly.sharedInstance().events().recordEvent(eventName, segmentation, count, sum, duration);
116+
}
117+
118+
Toast.makeText(this, "Event '" + eventName + "' recorded", Toast.LENGTH_SHORT).show();
34119
}
35120

36-
public void onClickRecordEvent05(View v) {
37-
Map<String, Object> segmentation = new ConcurrentHashMap<>();
38-
segmentation.put("wall", "green");
39-
Countly.sharedInstance().events().recordEvent("Custom event 5", segmentation, 1, 0, 0);
121+
private void startTimedEvent() {
122+
String name = inputTimedEventName.getText() != null ? inputTimedEventName.getText().toString().trim() : "";
123+
if (name.isEmpty()) {
124+
inputTimedEventName.setError("Event name is required");
125+
return;
126+
}
127+
boolean started = Countly.sharedInstance().events().startEvent(name);
128+
Toast.makeText(this, started ? "Timed event '" + name + "' started" : "Could not start (already running?)", Toast.LENGTH_SHORT).show();
40129
}
41130

42-
public void onClickRecordEvent06(View v) {
43-
Map<String, Object> segmentation = new ConcurrentHashMap<>();
44-
segmentation.put("wall", "red");
45-
segmentation.put("flowers", 3);
46-
segmentation.put("area", 1.23);
47-
segmentation.put("volume", 7.88);
48-
Countly.sharedInstance().events().recordEvent("Custom event 6", segmentation, 15, 0, 0);
131+
private void endTimedEvent() {
132+
String name = inputTimedEventName.getText() != null ? inputTimedEventName.getText().toString().trim() : "";
133+
if (name.isEmpty()) {
134+
inputTimedEventName.setError("Event name is required");
135+
return;
136+
}
137+
boolean ended = Countly.sharedInstance().events().endEvent(name);
138+
Toast.makeText(this, ended ? "Timed event '" + name + "' ended" : "Could not end (not running?)", Toast.LENGTH_SHORT).show();
49139
}
50140

51-
public void onClickRecordEvent07(View v) {
52-
Map<String, Object> segmentation = new ConcurrentHashMap<>();
53-
segmentation.put("wall", "blue");
54-
segmentation.put("flowers", new Random().nextInt());
55-
segmentation.put("area", new Random().nextDouble());
56-
segmentation.put("volume", new Random().nextDouble());
57-
58-
Countly.sharedInstance().events().recordEvent("Custom event 7", segmentation, 25, 10, 0);
141+
private void cancelTimedEvent() {
142+
String name = inputTimedEventName.getText() != null ? inputTimedEventName.getText().toString().trim() : "";
143+
if (name.isEmpty()) {
144+
inputTimedEventName.setError("Event name is required");
145+
return;
146+
}
147+
boolean cancelled = Countly.sharedInstance().events().cancelEvent(name);
148+
Toast.makeText(this, cancelled ? "Timed event '" + name + "' cancelled" : "Could not cancel (not running?)", Toast.LENGTH_SHORT).show();
59149
}
60150

61-
public void onClickRecordEvent08(View v) {
62-
Map<String, Object> segmentation = new ConcurrentHashMap<>();
63-
segmentation.put("wall", "yellow");
64-
Countly.sharedInstance().events().recordEvent("Custom event 8", segmentation, 25, 10, 50);
65-
}
66-
67-
public void onClickRecordEvent09(View v) {
68-
//start timed event
69-
Countly.sharedInstance().events().recordEvent("Custom event 9");
70-
}
71-
72-
public void onClickRecordEvent10(View v) {
73-
//stop timed event
74-
Countly.sharedInstance().events().recordEvent("Custom event 9");
75-
}
76-
77-
public void onClickRecordEvent12(View v) {
78-
//cancel timed event
79-
Countly.sharedInstance().events().cancelEvent("Custom event 9");
80-
}
81-
82-
public void onClickRecordEvent11(View v) {
83-
Map<String, Object> segmentation = new ConcurrentHashMap<>();
84-
segmentation.put("wall", "orange");
85-
Countly.sharedInstance().events().recordEvent("Custom event 9", segmentation, 4, 34);
151+
private void recordPresetWithSegmentation() {
152+
Map<String, Object> segmentation = new HashMap<>();
153+
segmentation.put("wall", "green");
154+
segmentation.put("flowers", 3);
155+
Countly.sharedInstance().events().recordEvent("Preset Segmentation Event", segmentation, 1, 0, 0);
156+
Toast.makeText(this, "Preset segmentation event recorded", Toast.LENGTH_SHORT).show();
86157
}
87158

88-
public void onClickTriggerSendingEvents(View v) {
89-
Countly.sharedInstance().requestQueue().attemptToSendStoredRequests();
159+
private void recordPresetWithCountSum() {
160+
Map<String, Object> segmentation = new HashMap<>();
161+
segmentation.put("wall", "blue");
162+
Countly.sharedInstance().events().recordEvent("Preset Count Sum Event", segmentation, 5, 24.5, 0);
163+
Toast.makeText(this, "Preset count+sum event recorded", Toast.LENGTH_SHORT).show();
90164
}
91165
}

0 commit comments

Comments
 (0)