Skip to content

Commit

Permalink
[Feature branch] The Contributions Dashboard (#5013)
Browse files Browse the repository at this point in the history
* [Feature branch] The Contributions Dashboard

* Update target languages and due date

* [Contributions dashboard] bottom nav tab rename (#5019)

* - adds conditional logic to support "Contribute" tab

* - adds a safeguard against potential crash
- adds an open function on NavTab to preserve the order after conditional addition of Tab
- replaces "Contributions" to "Edits" in the SuggestedEditsTasksFragment

* - fixes CI issue

* - adds conditional naming in the EDITS enum itself, removes the CONTRIBUTE enum which removes the potential crash and hidden side effects

* - fixes CI issue

* - removal of unused strings and some logic

---------

Co-authored-by: cooltey <[email protected]>

* [Contribution dashboard] Create a donor badge view

* View update

* Use TextView

* Add a proper padding to the chip icon for smaller text

* Update padding

* [Contributions dashboard] donor status on article overflow menu (#5028)

* - adds UI for displaying user donor status
- adds callback function for donor buttons
- adds preference for user visited donor history
-

* - renames Preference key and variable for clarity

* - rename functions for better clarity
- creates a single function for navigating to MainActivity with different tabs and extra

* - rename preference key

* - updates donor information text with string resource

* - adds developer preference for updating donor status

* - fix CI issues

* - replaces the XML with the custom DonorBadgeView and updates the code

* - fix ci issues

* - removes developer setting option for updating donor status

* - fix ci issue

* - adds developer setting for changing donor history

---------

Co-authored-by: cooltey <[email protected]>

* Add dialog message for donationResult preference

* [Contributions dashboard] survey dialog (#5044)

* - adds a survey dialog

* - adds a general simple alert dialog

* - converts the SimpleAlertDialog function to a class with easy to use extension for AppCompatActivity

* - adds contribution survey dialog to SurveyDialog object

* - adds survey link cta on alert dialog positive button

* - removes dialog from SurveyDialog object and adds it to the SuggestedEditsTasksFragment

* - adds contributionDashboardSurveyDialogShown preference

* - small code fix

* - code fixes

* - code fixes

---------

Co-authored-by: cooltey <[email protected]>

* Follow up: survey dialog for Contributions Dashboard

* [Contributions Dashboard] Donor History (#5021)

* Donor History screen

* Update strings

* Update strings

* Fix error

* Some viewModel stuff

* Remove old strings

* Some logic of initializing the view

* Build actions

* Finish the layout

* Datepicker

* Correctly apply theme for datepicker

* Show a proper time selection for the date picker

* More refine to datePicker

* Better selection status

* Add proper ripple animation

* Handle UTC issues in the date picker

* Disable if no date is selected

* Use tooltip view for a proper menu item color

* Add preference and comment for discussion

* Handle orientation properly

* Update design

* Update preference

* Move the preference to the bottom for easier access

* qq string

* Add link, paddings and ripple animation

* Code review comments

* Remove preference

* Checkbox

* Code review comments

* Fix datepicker text color

* Fix single item text color

* Add proper outline background

* Wire up the survey dialog

* Open link in an external browser

* [Contributions Dashboard] Update contribute tab for donor history (#5048)

* Initial commit for Contribute tab

* Update design

* Restore design

* TextView for toolbar title

* Use flexbox for edge case

* Add comment for log out logic and update flexbox layout

* Send a LoggedOutEvent to the FlowEventBus for refreshing screen purpose

* Get event and refresh

* Add minimal height

* Slighly change the top margin

* Rename layout and update the stats view to the new design

* Change to linearLayout

* Add donor history related view

* Margins

* Update/streamline layout of "last donated" row.

* Revert "Update/streamline layout of "last donated" row."

This reverts commit 5a453af.

* Update/streamline layout of "last donated" row. (#5062)

* Paddings

* Simplify the layout a bit

* Optimize

* Set date and clickable area

* Hide sequential tooltips

* Refine layout

* Update todo

* Streamline layout of title and donor button.

* Revert "Streamline layout of title and donor button."

This reverts commit 5917238.

* Streamline layout of title and donor button.

* Streamline layout of title and donor button. (#5066)

* Update layout

* Add donor status function to a proper location

* Remove unused layout parameters

* Fix style and wire up donor history

* Add donorHistory for overflow menu

---------

Co-authored-by: Dmitry Brant <[email protected]>
Co-authored-by: Dmitry Brant <[email protected]>

* Update Donor Status logic for recurring donor option

* Add warning dialog for modified page

* Save only if it is modified

* Add preference to dev preference

* [Contributions Dashboard] Entry dialogs for the feature (#5067)

* [Contributions Dashboard] Entry dialogs for the feature

* Update helper

* Wire entry dialogs logic

* Move dialog

* Add paramter

* Add temp variable

* Survey dialogs and update logic

---------

Co-authored-by: Dmitry Brant <[email protected]>

* Fix: make sure only refresh the content in the current fragment

* Fix: design signoff updates for Contribute Tab and Donor history (#5089)

* Fix: design signoff updates for Contribute Tab

* Adding fixes for donor history

* Fix entry dialog logic

* Fix: prevent possible crash

* Fix: set Donor badge icon to white

* Fix: Overflow menu design changes (#5095)

* - design fixes: reduces vertical padding for overflow menu and adds vertical padding to "update donor status" button for better accessibility

* - reverts back the padding

---------

Co-authored-by: cooltey <[email protected]>

* Add isRecurringDonor key to the dev preferences

* Fix: Update Contribute tab for design signoff comments (#5107)

* Update contribute tab for design signoff comments

* Add isRecurring donor preference to dev preferences

* Apply a check to avoid unnecessary loading

* Remove unused launcher

* Fix: donor status if taps on the already donated; replace double % with
the encoded symbol

* [Contributions dashboard] app icon  (#5055)

* - initial work and test work

* - adds list preference for setting app icon
- update manifest file to include activity alias for default icon
- shows snackbar when app icon changes

* - fixes both app icon showing on the home screen

* - adds a condition for showing donor icon
- adds TODO's

* - fixes CI issue

* - adds Bottom sheet dialog for App icon
- adds recyclerView for showing different app icon
- removes unused code
- adds styles for circular image view
- adds logic to select/de-select app icon

* - modifies AppIconDialog class to support API 21 for setting foreground and optimized code
- adds prefs for saving current selected app icon

* - adds logic to show/hide change app icon

* - fixes CI issue

* - adds donor benefit icon

* - replaces png with svg donor benefit launcher icon

* - code and ui fixes

* - ci fix

* - removes displayName from launcherIcon

* - adds missing logic for showing "App Icon" setting

* - replaces icon and removes unused resource

* - code fixes

---------

Co-authored-by: cooltey <[email protected]>

* Fix translation issue

* Revert "Fix translation issue"

This reverts commit e91fe45.

* Revert "Fix: donor status if taps on the already donated; replace double % with"

This reverts commit fb19f21.

* Update donor status logic

* [Contributions Dashboard] add isDonor preference for the optional donor (#5116)

info

* Use String.format() for the double percent symbol

* - disabled donor badge click when shown on SuggestedEditsTasksFragment (#5119)

* Fix: save the page even it is not modified

* Fix: Survey dialog design and logic changes (#5115)

* - design changes: centered title, icon tint to use secondary color
- logic changes:
1. when user updates donor history as a non donor the action on survey dialog opens snackbar
2. user updates donor history as donor then dialog actions opens thank you dialog

* - code and style fixes

* - centers the alert dialog title globally

---------

Co-authored-by: Cooltey Feng <[email protected]>

* Make container visibility

* Fix: make sure the username item only visible for the feature

* Show the snackbar for donor history saved

* Use correct string resource for app icon

* Design review: open AppIconDialog after clicking on "Take me there"

* Fix lint

* Fix: update click listener for donorHistoryUpdate button

* Refine the isDonor preference

* Fix: app icon "Donor Benefit" label border (#5121)

* - adds border to the "DONOR BENEFIT" design

* - renames file and replaces widget_color with border_color

* [Contributions Dashboard] Wire up the instrumentation (#5104)

* [Contributions Dashboard] Wire up the instrumentation

* Fix error

* Finalized instrumentation

* Add todo

* Pending: campaignId

* Use contrib for campaignId, remove anon suffix

* Remove space

* Oops

---------

Co-authored-by: William Rai <[email protected]>
Co-authored-by: Dmitry Brant <[email protected]>
Co-authored-by: Dmitry Brant <[email protected]>
Co-authored-by: williamrai <[email protected]>
  • Loading branch information
5 people authored Nov 14, 2024
1 parent c46dc78 commit ace469a
Show file tree
Hide file tree
Showing 57 changed files with 1,954 additions and 347 deletions.
29 changes: 27 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@
android:fullBackupContent="@xml/full_backup_rules"
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
android:icon="@mipmap/launcher"
android:label="@string/app_name"
android:name=".WikipediaApp"
android:theme="@style/AppTheme">
Expand All @@ -90,6 +89,11 @@
android:name=".main.MainActivity"
android:windowSoftInputMode="adjustResize"
android:theme="@style/AppTheme.Splash"
android:exported="true"/>
<activity-alias
android:name=".DefaultIcon"
android:targetActivity=".main.MainActivity"
android:icon="@mipmap/launcher"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -102,7 +106,25 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="wikipedia" />
</intent-filter>
</activity>
</activity-alias>
<activity-alias
android:name=".DonorIcon"
android:icon="@mipmap/ic_launcher_donor_benefit"
android:targetActivity=".main.MainActivity"
android:exported="true"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="wikipedia" />
</intent-filter>
</activity-alias>
<activity
android:name=".page.PageActivity"
android:windowSoftInputMode="stateAlwaysHidden|adjustPan"
Expand Down Expand Up @@ -371,6 +393,9 @@
<activity
android:name=".donate.GooglePayActivity"/>

<activity
android:name=".donate.DonorHistoryActivity" />

<provider
android:name=".WikipediaFileProvider"
android:authorities="${applicationId}.fileprovider"
Expand Down
57 changes: 57 additions & 0 deletions app/src/main/java/org/wikipedia/LauncherController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.wikipedia

import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
import org.wikipedia.settings.Prefs

object LauncherController {

fun setIcon(icon: LauncherIcon) {
val context = WikipediaApp.instance.applicationContext
val packageManager = context.packageManager
LauncherIcon.entries.forEach { launcherIcon ->
packageManager.setComponentEnabledSetting(
launcherIcon.getComponentName(context),
if (launcherIcon == icon) PackageManager.COMPONENT_ENABLED_STATE_ENABLED else
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
)
}
}
}

enum class LauncherIcon(
val key: String,
val background: Int,
val foreground: Int,
val label: Int,
var isSelected: Boolean = false
) {
DEFAULT(
key = "DefaultIcon",
background = R.drawable.launcher_background,
foreground = R.drawable.launcher_foreground,
label = R.string.app_name
),
DONOR(
key = "DonorIcon",
background = R.drawable.launcher_background,
foreground = R.drawable.ic_launcher_donor_benefit_foreground,
label = R.string.app_name
);

fun getComponentName(context: Context): ComponentName {
return ComponentName(context.packageName, "org.wikipedia.$key")
}

companion object {
fun initialValues(): List<LauncherIcon> {
val savedAppIcon = Prefs.currentSelectedAppIcon ?: DEFAULT.key
entries.forEach { icon ->
icon.isSelected = icon.key == savedAppIcon
}
return entries
}
}
}
8 changes: 7 additions & 1 deletion app/src/main/java/org/wikipedia/activity/BaseActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ import org.wikipedia.recurring.RecurringTasksExecutor
import org.wikipedia.richtext.CustomHtmlParser
import org.wikipedia.settings.Prefs
import org.wikipedia.theme.Theme
import org.wikipedia.usercontrib.ContributionsDashboardHelper
import org.wikipedia.util.DeviceUtil
import org.wikipedia.util.FeedbackUtil
import org.wikipedia.util.ResourceUtil
Expand All @@ -68,7 +69,12 @@ abstract class BaseActivity : AppCompatActivity(), ConnectionStateMonitor.Callba
private val requestDonateActivity = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
ExclusiveBottomSheetPresenter.dismiss(supportFragmentManager)
FeedbackUtil.showMessage(this, R.string.donate_gpay_success_message)
if (!Prefs.contributionsDashboardEntryDialogShown && ContributionsDashboardHelper.contributionsDashboardEnabled) {
ContributionsDashboardHelper.showDonationCompletedDialog(this)
Prefs.contributionsDashboardEntryDialogShown = true
} else {
FeedbackUtil.showMessage(this, R.string.donate_gpay_success_message)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.wikipedia.analytics.eventplatform

import org.wikipedia.WikipediaApp
import org.wikipedia.dataclient.donate.CampaignCollection
import org.wikipedia.settings.Prefs
import org.wikipedia.usercontrib.ContributionsDashboardHelper

class ContributionsDashboardEvent : DonorExperienceEvent() {

companion object {

fun logAction(
action: String,
activeInterface: String,
wikiId: String = WikipediaApp.instance.appOrSystemLanguageCode,
campaignId: String? = null
) {
if (ContributionsDashboardHelper.contributionsDashboardEnabled) {
submit(
action,
activeInterface,
campaignId?.let { "campaign_id: ${CampaignCollection.getFormattedCampaignId(it)}, " }
.orEmpty() + "donor_detected: ${Prefs.isDonor}",
wikiId
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import org.wikipedia.WikipediaApp
import org.wikipedia.dataclient.donate.CampaignCollection
import org.wikipedia.settings.Prefs

class DonorExperienceEvent {
open class DonorExperienceEvent {

companion object {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ object CampaignCollection {

fun addDonationResult(fromWeb: Boolean = false) {
Prefs.donationResults = Prefs.donationResults.plus(DonationResult(dateTime = LocalDateTime.now().toString(), fromWeb = fromWeb))
Prefs.isDonor = true
Prefs.hasDonorHistorySaved = true
}

@Serializable
Expand Down
214 changes: 214 additions & 0 deletions app/src/main/java/org/wikipedia/donate/DonorHistoryActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package org.wikipedia.donate

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.format.DateUtils
import androidx.activity.viewModels
import androidx.core.view.isVisible
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.DateValidatorPointBackward
import com.google.android.material.datepicker.MaterialDatePicker
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.wikipedia.Constants
import org.wikipedia.R
import org.wikipedia.activity.BaseActivity
import org.wikipedia.analytics.eventplatform.ContributionsDashboardEvent
import org.wikipedia.databinding.ActivityDonorHistoryBinding
import org.wikipedia.main.MainActivity
import org.wikipedia.settings.Prefs
import org.wikipedia.usercontrib.ContributionsDashboardHelper
import org.wikipedia.util.ResourceUtil
import org.wikipedia.util.UriUtil
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZoneOffset
import java.time.ZonedDateTime

class DonorHistoryActivity : BaseActivity() {

private lateinit var binding: ActivityDonorHistoryBinding
private val viewModel: DonorHistoryViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDonorHistoryBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
supportActionBar?.title = getString(R.string.donor_history_title)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
init()
}

override fun onBackPressed() {
if (viewModel.donorHistoryModified) {
MaterialAlertDialogBuilder(this)
.setMessage(getString(R.string.edit_abandon_confirm))
.setPositiveButton(getString(R.string.edit_abandon_confirm_yes)) { dialog, _ ->
ContributionsDashboardEvent.logAction("cancel_click", "contrib_update")
dialog.dismiss()
if (viewModel.shouldGoBackToContributeTab) {
startActivity(MainActivity.newIntent(this).putExtra(Constants.INTENT_EXTRA_GO_TO_SE_TAB, true))
} else {
finish()
}
}
.setNegativeButton(getString(R.string.edit_abandon_confirm_no)) { dialog, _ ->
ContributionsDashboardEvent.logAction("restart_click", "contrib_update")
dialog.dismiss()
}
.show()
return
}
super.onBackPressed()
}

private fun init() {

binding.donationInfoContainer.isVisible = viewModel.isDonor

binding.donorStatus.setOnClickListener {
ContributionsDashboardEvent.logAction("update_click", "contrib_update")
showDonorStatusDialog()
}

binding.lastDonationContainer.setOnClickListener {
showLastDonatedDatePicker()
}

binding.recurringDonorCheckbox.isChecked = viewModel.isRecurringDonor
binding.recurringDonorCheckbox.setOnClickListener {
viewModel.donorHistoryModified = true
viewModel.isRecurringDonor = binding.recurringDonorCheckbox.isChecked
binding.recurringDonorCheckbox.isChecked = viewModel.isRecurringDonor
}
binding.recurringDonorContainer.setOnClickListener {
binding.recurringDonorCheckbox.performClick()
}

binding.donateButton.setOnClickListener {
ContributionsDashboardEvent.logAction("donate_start_click", "contrib_update", campaignId = ContributionsDashboardHelper.CAMPAIGN_ID)
launchDonateDialog(campaignId = ContributionsDashboardHelper.CAMPAIGN_ID)
}

binding.experimentLink.setOnClickListener {
ContributionsDashboardEvent.logAction("about_click", "contrib_update")
UriUtil.visitInExternalBrowser(this, Uri.parse(getString(R.string.contributions_dashboard_wiki_url)))
}

binding.saveButton.setOnClickListener {
ContributionsDashboardEvent.logAction("save_click", "contrib_update")
viewModel.saveDonorHistory()
if (viewModel.shouldGoBackToContributeTab) {
startActivity(
MainActivity.newIntent(this)
.putExtra(Constants.INTENT_EXTRA_GO_TO_SE_TAB, true)
)
return@setOnClickListener
}
finish()
}
updateDonorStatusText()
updateLastDonatedText()
}

private fun updateDonorStatusText() {
var donorStatusTextColor = R.attr.primary_color
val donorStatusText = if (!Prefs.hasDonorHistorySaved && viewModel.currentDonorStatus == -1) {
donorStatusTextColor = R.attr.placeholder_color
R.string.donor_history_update_donor_status_default
} else if (viewModel.isDonor) {
viewModel.currentDonorStatus = 0
R.string.donor_history_update_donor_status_donor
} else {
viewModel.currentDonorStatus = 1
R.string.donor_history_update_donor_status_not_a_donor
}
binding.donorStatus.text = getString(donorStatusText)
binding.donorStatus.setTextColor(ResourceUtil.getThemedColorStateList(this, donorStatusTextColor))
binding.donateButton.isVisible = viewModel.currentDonorStatus == 1 // Not a donor
binding.donationInfoContainer.isVisible = viewModel.isDonor
}

private fun updateLastDonatedText() {
binding.lastDonationDate.isVisible = viewModel.lastDonated != null
var lastDonatedTextColor = R.attr.primary_color
val lastDonatedText = if (viewModel.lastDonated == null) {
lastDonatedTextColor = R.attr.placeholder_color
R.string.donor_history_last_donated_hint
} else {
R.string.donor_history_last_donated
}
binding.lastDonationLabel.text = getString(lastDonatedText)
binding.lastDonationLabel.setTextColor(ResourceUtil.getThemedColorStateList(this, lastDonatedTextColor))
viewModel.lastDonated?.let {
binding.lastDonationDate.text = DateUtils.getRelativeTimeSpanString(
viewModel.dateTimeToMilli(it),
System.currentTimeMillis(),
DateUtils.DAY_IN_MILLIS
)
}
}

private fun showDonorStatusDialog() {
val donorStatusList = arrayOf(
getString(R.string.donor_history_update_donor_status_donor),
getString(R.string.donor_history_update_donor_status_not_a_donor)
)
MaterialAlertDialogBuilder(this)
.setSingleChoiceItems(donorStatusList, viewModel.currentDonorStatus) { dialog, which ->
viewModel.isDonor = which == 0
viewModel.currentDonorStatus = which
viewModel.donorHistoryModified = true
updateDonorStatusText()
updateLastDonatedText()
dialog.dismiss()
}
.show()
}

private fun showLastDonatedDatePicker() {
// The CalendarConstraints handles date in UTC
val utcMillis = LocalDateTime.now().toInstant(ZoneOffset.UTC).toEpochMilli()
val defaultDatePickerMilli = viewModel.lastDonated?.let {
viewModel.dateTimeToMilli(it)
} ?: run {
utcMillis
}

val calendarConstraints = CalendarConstraints.Builder()
.setEnd(utcMillis)
.setValidator(DateValidatorPointBackward.before(utcMillis))
.build()

MaterialDatePicker.Builder.datePicker()
.setTheme(R.style.MaterialDatePickerStyle)
.setSelection(defaultDatePickerMilli)
.setInputMode(MaterialDatePicker.INPUT_MODE_TEXT)
.setCalendarConstraints(calendarConstraints)
.build()
.apply {
addOnPositiveButtonClickListener {
// The date picker returns milliseconds in UTC timezone.
val utcDate = LocalDateTime.ofInstant(Instant.ofEpochMilli(it), ZoneOffset.UTC)
viewModel.lastDonated = ZonedDateTime.of(utcDate, ZoneId.systemDefault()).toLocalDateTime().toString()
viewModel.donorHistoryModified = true
updateLastDonatedText()
}
}
.show(supportFragmentManager, "datePicker")
}

companion object {

const val RESULT_GO_BACK_TO_CONTRIBUTE_TAB = "goBackToContributeTab"

fun newIntent(context: Context, completedDonation: Boolean = false, goBackToContributeTab: Boolean = false): Intent {
return Intent(context, DonorHistoryActivity::class.java)
.putExtra(Constants.ARG_BOOLEAN, completedDonation)
.putExtra(RESULT_GO_BACK_TO_CONTRIBUTE_TAB, goBackToContributeTab)
}
}
}
Loading

0 comments on commit ace469a

Please sign in to comment.