Skip to content

Commit

Permalink
Merge pull request #4559 from vector-im/feature/bma/posthog
Browse files Browse the repository at this point in the history
Analytics
  • Loading branch information
bmarty authored Dec 14, 2021
2 parents fa06005 + be33a53 commit f4cfb5d
Show file tree
Hide file tree
Showing 71 changed files with 2,166 additions and 48 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/sync-from-external-sources.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,27 @@ jobs:
body: |
- Update SAS Strings from matrix-doc.
branch: sync-sas-strings
base: develop

sync-analytics-plan:
runs-on: ubuntu-latest
# Skip in forks
if: github.repository == 'vector-im/element-android'
steps:
- uses: actions/checkout@v2
- name: Run analytics import script
run: ./tools/import_analytic_plan.sh
- name: Create Pull Request for analytics plan
uses: peter-evans/create-pull-request@v3
with:
commit-message: Sync analytics plan
title: Sync analytics plan
body: |
### Update analytics plan
Reviewers:
- [ ] Please remove usage of Event or Enum which may have been removed or updated
- [ ] please ensure new Events or new Enums are used to send analytics by pushing new commit(s) to this PR.
*Note*: Change are coming from [this project](https://github.com/matrix-org/matrix-analytics-events)
branch: sync-analytics-plan
base: develop
1 change: 1 addition & 0 deletions .idea/dictionaries/bmarty.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dependencies_groups.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ ext.groups = [
'com.parse.bolts',
'com.pinterest',
'com.pinterest.ktlint',
'com.posthog.android',
'com.squareup',
'com.squareup.duktape',
'com.squareup.moshi',
Expand Down
16 changes: 16 additions & 0 deletions docs/analytics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Analytics in Element

## Solution

Element is using PostHog to send analytics event.
We ask for the user to give consent before sending any analytics data.

## How to add a new Event

The analytics plan is shared between all Element clients. To add an Event, please open a PR to this project: https://github.com/matrix-org/matrix-analytics-events

Then, once the PR has been merged, you can run the tool `import_analytic_plan.sh` to import the plan to Element, and then you can use the new Event. Note that this tool is run by Github action once a week.

## Forks of Element

Analytics on forks are disabled by default. Please refer to AnalyticsConfig and there implementation to setup analytics on your project.
6 changes: 6 additions & 0 deletions library/ui-styles/src/main/res/values-sw600dp/tablet.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<dimen name="width_percent">0.6</dimen>

</resources>
6 changes: 6 additions & 0 deletions library/ui-styles/src/main/res/values-sw720dp/tablet.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<dimen name="width_percent">0.5</dimen>

</resources>
6 changes: 6 additions & 0 deletions library/ui-styles/src/main/res/values/tablet.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>

<dimen name="width_percent">1</dimen>

</resources>
2 changes: 1 addition & 1 deletion tools/check/forbidden_strings_in_code.txt
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Formatter\.formatShortFileSize===1
# android\.text\.TextUtils

### This is not a rule, but a warning: the number of "enum class" has changed. For Json classes, it is mandatory that they have `@JsonClass(generateAdapter = false)`. If the enum is not used as a Json class, change the value in file forbidden_strings_in_code.txt
enum class===110
enum class===114

### Do not import temporary legacy classes
import org.matrix.android.sdk.internal.legacy.riot===3
Expand Down
18 changes: 18 additions & 0 deletions tools/import_analytic_plan.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash

echo "Deleted existing plan..."
rm vector/src/main/java/im/vector/app/features/analytics/plan/*.*

echo "Cloning analytics project..."
mkdir analytics_tmp
cd analytics_tmp
git clone https://github.com/matrix-org/matrix-analytics-events.git

echo "Copy plan..."
cp matrix-analytics-events/types/kotlin2/* ../vector/src/main/java/im/vector/app/features/analytics/plan/

echo "Cleanup."
cd ..
rm -rf analytics_tmp

echo "Done."
3 changes: 3 additions & 0 deletions vector/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@ dependencies {
implementation libs.dagger.hilt
kapt libs.dagger.hiltCompiler

// Analytics
implementation 'com.posthog.android:posthog:1.1.2'

// gplay flavor only
gplayImplementation('com.google.firebase:firebase-messaging:23.0.0') {
exclude group: 'com.google.firebase', module: 'firebase-core'
Expand Down
1 change: 1 addition & 0 deletions vector/src/debug/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<application>
<activity android:name=".features.debug.TestLinkifyActivity" />
<activity android:name=".features.debug.DebugPermissionActivity" />
<activity android:name=".features.debug.analytics.DebugAnalyticsActivity" />
<activity android:name=".features.debug.settings.DebugPrivateSettingsActivity" />
<activity android:name=".features.debug.sas.DebugSasEmojiActivity" />
<activity android:name=".features.debug.features.DebugFeaturesSettingsActivity" />
Expand Down
27 changes: 27 additions & 0 deletions vector/src/debug/java/im/vector/app/config/AnalyticsConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* 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 im.vector.app.config

import im.vector.app.BuildConfig
import im.vector.app.features.analytics.AnalyticsConfig

val analyticsConfig: AnalyticsConfig = object : AnalyticsConfig {
override val isEnabled = BuildConfig.APPLICATION_ID == "im.vector.app.debug"
override val postHogHost = "https://posthog-poc.lab.element.dev"
override val postHogApiKey = "rs-pJjsYJTuAkXJfhaMmPUNBhWliDyTKLOOxike6ck8"
override val policyLink = "https://element.io/cookie-policy"
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.toast
import im.vector.app.databinding.ActivityDebugMenuBinding
import im.vector.app.features.debug.analytics.DebugAnalyticsActivity
import im.vector.app.features.debug.features.DebugFeaturesSettingsActivity
import im.vector.app.features.debug.sas.DebugSasEmojiActivity
import im.vector.app.features.debug.settings.DebugPrivateSettingsActivity
Expand Down Expand Up @@ -79,6 +80,9 @@ class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
private fun setupViews() {
views.debugFeatures.setOnClickListener { startActivity(Intent(this, DebugFeaturesSettingsActivity::class.java)) }
views.debugPrivateSetting.setOnClickListener { openPrivateSettings() }
views.debugAnalytics.setOnClickListener {
startActivity(Intent(this, DebugAnalyticsActivity::class.java))
}
views.debugTestTextViewLink.setOnClickListener { testTextViewLink() }
views.debugOpenButtonStylesLight.setOnClickListener {
startActivity(Intent(this, DebugVectorButtonStylesLightActivity::class.java))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* 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 im.vector.app.features.debug.analytics

import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding

@AndroidEntryPoint
class DebugAnalyticsActivity : VectorBaseActivity<ActivitySimpleBinding>() {

override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)

override fun initUiAndData() {
if (isFirstCreation()) {
addFragment(
views.simpleFragmentContainer,
DebugAnalyticsFragment::class.java
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* 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 im.vector.app.features.debug.analytics

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.toOnOff
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentDebugAnalyticsBinding
import me.gujun.android.span.span

class DebugAnalyticsFragment : VectorBaseFragment<FragmentDebugAnalyticsBinding>() {

private val viewModel: DebugAnalyticsViewModel by fragmentViewModel()

override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentDebugAnalyticsBinding {
return FragmentDebugAnalyticsBinding.inflate(inflater, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

setViewListeners()
}

private fun setViewListeners() {
views.showAnalyticsOptIn.onClick {
navigator.openAnalyticsOptIn(requireContext())
}
views.resetAnalyticsOptInDisplayed.onClick {
viewModel.handle(DebugAnalyticsViewActions.ResetAnalyticsOptInDisplayed)
}
}

override fun invalidate() = withState(viewModel) { state ->
views.analyticsStoreContent.text = span {
+"AnalyticsId: "
span {
textStyle = "bold"
text = state.analyticsId.orEmpty()
}
+"\nOptIn: "
span {
textStyle = "bold"
text = state.userConsent.toOnOff()
}
+"\nDidAsk: "
span {
textStyle = "bold"
text = state.didAskUserConsent.toString()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* 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 im.vector.app.features.debug.analytics

import im.vector.app.core.platform.VectorViewModelAction

sealed interface DebugAnalyticsViewActions : VectorViewModelAction {
object ResetAnalyticsOptInDisplayed : DebugAnalyticsViewActions
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* 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 im.vector.app.features.debug.analytics

import com.airbnb.mvrx.MavericksViewModelFactory
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.analytics.store.AnalyticsStore
import kotlinx.coroutines.launch

class DebugAnalyticsViewModel @AssistedInject constructor(
@Assisted initialState: DebugAnalyticsViewState,
private val analyticsStore: AnalyticsStore
) : VectorViewModel<DebugAnalyticsViewState, DebugAnalyticsViewActions, EmptyViewEvents>(initialState) {

@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<DebugAnalyticsViewModel, DebugAnalyticsViewState> {
override fun create(initialState: DebugAnalyticsViewState): DebugAnalyticsViewModel
}

companion object : MavericksViewModelFactory<DebugAnalyticsViewModel, DebugAnalyticsViewState> by hiltMavericksViewModelFactory()

init {
observerStore()
}

private fun observerStore() {
analyticsStore.analyticsIdFlow.setOnEach { copy(analyticsId = it) }
analyticsStore.userConsentFlow.setOnEach { copy(userConsent = it) }
analyticsStore.didAskUserConsentFlow.setOnEach { copy(didAskUserConsent = it) }
}

override fun handle(action: DebugAnalyticsViewActions) {
when (action) {
DebugAnalyticsViewActions.ResetAnalyticsOptInDisplayed -> handleResetAnalyticsOptInDisplayed()
}.exhaustive
}

private fun handleResetAnalyticsOptInDisplayed() {
viewModelScope.launch {
analyticsStore.setDidAskUserConsent(false)
}
}
}
Loading

0 comments on commit f4cfb5d

Please sign in to comment.