Skip to content

Commit

Permalink
Updated OverviewActivity. and added ReadMorePlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
aanorbel committed Oct 25, 2023
1 parent 657a6c9 commit 6fd4edf
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import org.openobservatory.ooniprobe.R;
import org.openobservatory.ooniprobe.common.PreferenceManager;
import org.openobservatory.ooniprobe.common.ReadMorePlugin;
import org.openobservatory.ooniprobe.databinding.ActivityOverviewBinding;
import org.openobservatory.ooniprobe.model.database.Result;
import org.openobservatory.ooniprobe.test.suite.AbstractSuite;
Expand Down Expand Up @@ -50,11 +51,9 @@ public static Intent newIntent(Context context, AbstractSuite testSuite) {
setTitle(testSuite.getTitle());
binding.icon.setImageResource(testSuite.getIcon());
binding.customUrl.setVisibility(testSuite.getName().equals(WebsitesSuite.NAME) ? View.VISIBLE : View.GONE);
if(testSuite.isTestEmpty(preferenceManager)){
binding.run.setAlpha(0.5F);
binding.run.setEnabled(false);
}
Markwon markwon = Markwon.builder(this).build();
Markwon markwon = Markwon.builder(this)
.usePlugin(new ReadMorePlugin())
.build();
if (testSuite.getName().equals(ExperimentalSuite.NAME)) {
String experimentalLinks =
"\n\n* [STUN Reachability](https://github.com/ooni/spec/blob/master/nettests/ts-025-stun-reachability.md)" +
Expand All @@ -78,7 +77,6 @@ public static Intent newIntent(Context context, AbstractSuite testSuite) {
}

private void setUpOnCLickListeners() {
binding.run.setOnClickListener(view -> onRunClick());
binding.customUrl.setOnClickListener(view -> customUrlClick());
}

Expand All @@ -95,12 +93,6 @@ public boolean onSupportNavigateUp() {
return true;
}

void onRunClick() {
if(!testSuite.isTestEmpty(preferenceManager)){
RunningActivity.runAsForegroundService(this, testSuite.asArray(), this::bindTestService, preferenceManager);
}
}

void customUrlClick() {
startActivity(new Intent(this, CustomWebsiteActivity.class));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package org.openobservatory.ooniprobe.common

import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.ClickableSpan
import android.text.style.ReplacementSpan
import android.view.View
import android.widget.TextView
import io.noties.markwon.AbstractMarkwonPlugin


/**
* Read more plugin based on text length.
* @see <a href="https://github.com/noties/Markwon/blob/v4.6.2/app-sample/src/main/java/io/noties/markwon/app/samples/ReadMorePluginSample.java#L208C2-L208C2">ReadMorePluginSample</a>
*/
class ReadMorePlugin : AbstractMarkwonPlugin() {
private val maxLength = 150
private val labelMore = "\n\nRead more >"
private val labelLess = "\n\nRead less >"

override fun afterSetText(textView: TextView) {
val text = textView.text
if (text.length < maxLength) {
// everything is OK, no need to ellipsize)
return
}
val breakAt = breakTextAt(text, 0, maxLength)
val cs = createCollapsedString(text, 0, breakAt)
textView.text = cs
}

private fun createCollapsedString(text: CharSequence, start: Int, end: Int): CharSequence {
val builder = SpannableStringBuilder(text, start, end)

// NB! each table row is represented as a space character and new-line (so length=2) no
// matter how many characters are inside table cells

// we can _clean_ this builder, for example remove all dynamic content (like images and tables,
// but keep them in full/expanded version)
if (true) {
// it is an implementation detail but _mostly_ dynamic content is implemented as
// ReplacementSpans
val spans = builder.getSpans(
0, builder.length,
ReplacementSpan::class.java
)
if (spans != null) {
for (span in spans) {
builder.removeSpan(span)
}
}

// NB! if there will be a table in _preview_ (collapsed) then each row will be represented as a
// space and new-line
trim(builder)
}
val fullText = createFullText(text, builder)
builder.append(" ...")
val length = builder.length
builder.append(labelMore)
builder.setSpan(object : ClickableSpan() {
override fun onClick(widget: View) {
(widget as TextView).text = fullText
}
}, length, builder.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
return builder
}

private fun createFullText(text: CharSequence, collapsedText: CharSequence): CharSequence {
// full/expanded text can also be different,
// for example it can be kept as-is and have no `collapse` functionality (once expanded cannot collapse)
// or can contain collapse feature
val fullText: CharSequence
if (true) {
// for example, let's allow collapsing
val builder = SpannableStringBuilder(text)
builder.append(' ')
val length = builder.length
builder.append(labelLess)
builder.setSpan(object : ClickableSpan() {
override fun onClick(widget: View) {
(widget as TextView).text = collapsedText
}
}, length, builder.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
fullText = builder
} else {
fullText = text
}
return fullText
}

companion object {
private fun trim(builder: SpannableStringBuilder) {

// NB! tables use `\u00a0` (non breaking space) which is not reported as white-space
var c: Char
run {
var i = 0
val length = builder.length
while (i < length) {
c = builder[i]
if (!Character.isWhitespace(c) && c != '\u00a0') {
if (i > 0) {
builder.replace(0, i, "")
}
break
}
i++
}
}
for (i in builder.length - 1 downTo 0) {
c = builder[i]
if (!Character.isWhitespace(c) && c != '\u00a0') {
if (i < builder.length - 1) {
builder.replace(i, builder.length, "")
}
break
}
}
}

// depending on your locale these can be different
// There is a BreakIterator in Android, but it is not reliable, still theoretically
// it should work better than hand-written and hardcoded rules
private fun breakTextAt(text: CharSequence, start: Int, max: Int): Int {
var last = start

// no need to check for _start_ (anyway will be ignored)
for (i in start + max - 1 downTo start + 1) {
val c = text[i]
if (Character.isWhitespace(c) || c == '.' || c == ',' || c == '!' || c == '?') {
// include this special character
last = i - 1
break
}
}
return if (last <= start) {
// when used in subSequence last index is exclusive,
// so given max=150 would result in 0-149 subSequence
start + max
} else last
}
}
}
13 changes: 0 additions & 13 deletions app/src/main/res/layout/activity_overview.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,6 @@
</LinearLayout>
</LinearLayout>

<Button
android:id="@+id/run"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/Dashboard_Card_Run"
android:textAllCaps="false"
android:textColor="?attr/colorPrimary"
android:transitionName="@string/transitionNameRun"
app:backgroundTint="@android:color/white"
app:cornerRadius="24dp"
app:rippleColor="@color/ripple_material_light" />

<Button
android:id="@+id/customUrl"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
Expand Down
3 changes: 2 additions & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#Wed Oct 25 19:25:06 WAT 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

0 comments on commit 6fd4edf

Please sign in to comment.