Skip to content
This repository was archived by the owner on May 10, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.github.sisyphsu.dateparser.DateParser;
import com.luckycatlabs.sunrisesunset.SunriseSunsetCalculator;
import com.luckycatlabs.sunrisesunset.dto.Location;
import com.stario.launcher.R;
Expand Down Expand Up @@ -345,7 +344,6 @@ void assign(double value) {

private final BroadcastReceiver receiver;
private final WeatherPreview preview;
private final DateParser dateParser;

private SharedPreferences weatherPreferences;
private GeocoderFallback geocoder;
Expand All @@ -364,7 +362,6 @@ void assign(double value) {

public Weather() {
this.weatherData = new CopyOnWriteArrayList<>();
this.dateParser = DateParser.newBuilder().build();
this.runningTask = null;
this.address = null;
this.lastUpdate = 0;
Expand Down Expand Up @@ -550,7 +547,7 @@ public synchronized void update() {

String iconCode = summary.getString("symbol_code");

entries.add(new Data(dateParser.parseDate(time), iconCode,
entries.add(new Data(Utils.parseDate(time), iconCode,
temperature, windDirection, windSpeed));
} catch (JSONException exception) {
Log.e(TAG, "update: Parse exception for item " + index + ".");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,19 @@
import com.stario.launcher.R;
import com.stario.launcher.Stario;
import com.stario.launcher.preferences.Vibrations;
import com.stario.launcher.ui.common.text.LinkMovementMethodWithFallback;
import com.stario.launcher.ui.common.text.ClickableSpanTextView;
import com.stario.launcher.ui.utils.animation.Animation;
import com.stario.launcher.utils.Utils;

import org.jsoup.Jsoup;
import org.jsoup.safety.Safelist;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;

class FeedPageAdapter extends RecyclerView.Adapter<FeedPageAdapter.ViewHolder> {
private static final Safelist CONTENT_SAFELIST = new Safelist() {
Expand Down Expand Up @@ -105,12 +108,13 @@ public boolean shouldUpdate() {
}

protected class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final ImageView display;
private final ClickableSpanTextView description;
private final ViewGroup representative;
private final TextView title;
private final TextView description;
private final TextView author;
private final TextView timestamp;
private final ImageView display;
private final TextView category;
private final TextView author;
private final TextView title;

public ViewHolder(View itemView) {
super(itemView);
Expand All @@ -119,17 +123,16 @@ public ViewHolder(View itemView) {
representative = itemView.findViewById(R.id.representative);
title = itemView.findViewById(R.id.title);
description = itemView.findViewById(R.id.description);
timestamp = itemView.findViewById(R.id.timestamp);
author = itemView.findViewById(R.id.author);
category = itemView.findViewById(R.id.category);

itemView.setClipToOutline(true);
itemView.setOnClickListener(this);

description.setMovementMethod(new LinkMovementMethodWithFallback() {
@Override
public void onClickFallback(View widget) {
itemView.performClick();
}
description.setOnSpanClickListener((view, span) -> {
Vibrations.getInstance().vibrate();
span.onClick(view);
});
}

Expand Down Expand Up @@ -167,6 +170,7 @@ public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
viewHolder.author.setVisibility(View.GONE);
viewHolder.category.setVisibility(View.GONE);
viewHolder.description.setVisibility(View.GONE);
viewHolder.timestamp.setVisibility(View.GONE);
viewHolder.display.setAlpha(0f);

String image = item.getImage();
Expand Down Expand Up @@ -242,6 +246,24 @@ public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model
viewHolder.author.setVisibility(View.VISIBLE);
}
}

if (item.getPubDate() != null && !item.getPubDate().isEmpty()) {
try {
Date date = Utils.parseDate(item.getPubDate());
SimpleDateFormat output =
new SimpleDateFormat("dd MMM yyyy • HH:mm", Locale.getDefault());

if (date != null) {
viewHolder.timestamp.setText(output.format(date));
} else {
viewHolder.timestamp.setText(item.getPubDate());
}
} catch (Exception exception) {
viewHolder.timestamp.setText(item.getPubDate());
} finally {
viewHolder.timestamp.setVisibility(View.VISIBLE);
}
}
}

private Spanned cleanHtml(String html) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Copyright (C) 2026 Răzvan Albu
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/

package com.stario.launcher.ui.common.text;

import android.annotation.SuppressLint;
import android.content.Context;
import android.text.Layout;
import android.text.Spanned;
import android.text.style.ClickableSpan;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;

// Modification of https://stackoverflow.com/a/52373765
public class ClickableSpanTextView extends AppCompatTextView implements View.OnTouchListener {
private OnSpanClickListener spanClickListener;
private Runnable longPressRunnable;
private boolean longPressTriggered;
private ClickableSpan pressedSpan;
private int moveSlop;
private float downX;
private float downY;

public ClickableSpanTextView(@NonNull Context context) {
super(context);

init(context);
}

public ClickableSpanTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);

init(context);
}

public ClickableSpanTextView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);

init(context);
}

@SuppressLint("ClickableViewAccessibility")
private void init(Context context) {
super.setOnTouchListener(this);

longPressRunnable = () -> longPressTriggered = true;
moveSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}

@Override
public void setOnTouchListener(OnTouchListener l) {
throw new RuntimeException("ClickableSpanTextView cannot set a touch listener.");
}

@Override
public boolean onTouch(View view, MotionEvent event) {
if (!(getText() instanceof Spanned)) {
return false;
}

Spanned text = (Spanned) getText();
int action = event.getAction();

switch (action) {
case MotionEvent.ACTION_DOWN: {
pressedSpan = findSpan(event, text);

if (pressedSpan != null) {
downX = event.getX();
downY = event.getY();
longPressTriggered = false;

postDelayed(longPressRunnable,
ViewConfiguration.getLongPressTimeout());

return true;
}

break;
}

case MotionEvent.ACTION_MOVE: {
if (pressedSpan == null) break;

float dx = Math.abs(event.getX() - downX);
float dy = Math.abs(event.getY() - downY);

if (dx > moveSlop || dy > moveSlop) {
cancelPressedSpan();
}

break;
}

case MotionEvent.ACTION_CANCEL: {
cancelPressedSpan();
break;
}

case MotionEvent.ACTION_UP: {
if (pressedSpan != null && !longPressTriggered) {
if (spanClickListener != null) {
spanClickListener.onSpanClick(this, pressedSpan);
cancelPressedSpan();

return true;
}
}

cancelPressedSpan();
break;
}
}

return false;
}

private void cancelPressedSpan() {
removeCallbacks(longPressRunnable);

pressedSpan = null;
longPressTriggered = false;
}

private ClickableSpan findSpan(MotionEvent event, Spanned spannable) {
int x = (int) event.getX();
int y = (int) event.getY();

x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();

x += getScrollX();
y += getScrollY();

Layout layout = getLayout();
if (layout == null) {
return null;
}

int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);

ClickableSpan[] spans = spannable.getSpans(off, off, ClickableSpan.class);
return spans.length > 0 ? spans[0] : null;
}

public void setOnSpanClickListener(OnSpanClickListener listener) {
this.spanClickListener = listener;
}

public interface OnSpanClickListener {
void onSpanClick(@NonNull ClickableSpanTextView view, @NonNull ClickableSpan span);
}
}

This file was deleted.

Loading
Loading