Skip to content
This repository has been archived by the owner on Mar 28, 2024. It is now read-only.

Add home widget feature displaying the first four missing anime #235

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
1 change: 1 addition & 0 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,5 @@ flutter {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
}
8 changes: 8 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
</intent-filter>
</activity>

<receiver android:name="HomeWidgetProvider" android:exported="false" android:label="Watchlist">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/home_widget_provider" />
</receiver>

<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-5658764393995798~7443712880"/>
Expand Down
74 changes: 74 additions & 0 deletions android/app/src/main/kotlin/fr/ziedelth/jais/HomeWidgetProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package fr.ziedelth.jais

import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.SharedPreferences
import android.graphics.BitmapFactory
import android.net.Uri
import android.view.View
import android.widget.RemoteViews

import es.antonborri.home_widget.HomeWidgetBackgroundIntent
import es.antonborri.home_widget.HomeWidgetLaunchIntent
import es.antonborri.home_widget.HomeWidgetProvider

class HomeWidgetProvider : HomeWidgetProvider() {
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, widgetData: SharedPreferences) {
appWidgetIds.forEach { widgetId ->
val views = RemoteViews(context.packageName, R.layout.home_widget_layout).apply {
// Open App on Widget Click
val pendingIntent = HomeWidgetLaunchIntent.getActivity(
context,
MainActivity::class.java)
setOnClickPendingIntent(R.id.home_widget_container, pendingIntent)

// Show Images saved with `renderFlutterWidget`
val image1 = widgetData.getString("image0", null)

if (image1 != null) {
setImageViewBitmap(R.id.widget_img1, BitmapFactory.decodeFile(image1))
setViewVisibility(R.id.widget_img1, View.VISIBLE)
} else {
setViewVisibility(R.id.widget_img1, View.GONE)
}

val image2 = widgetData.getString("image1", null)

if (image2 != null) {
setImageViewBitmap(R.id.widget_img2, BitmapFactory.decodeFile(image2))
setViewVisibility(R.id.widget_img2, View.VISIBLE)
} else {
setViewVisibility(R.id.widget_img2, View.GONE)
}

val image3 = widgetData.getString("image2", null)

if (image3 != null) {
setImageViewBitmap(R.id.widget_img3, BitmapFactory.decodeFile(image3))
setViewVisibility(R.id.widget_img3, View.VISIBLE)
} else {
setViewVisibility(R.id.widget_img3, View.GONE)
}

val image4 = widgetData.getString("image3", null)

if (image4 != null) {
setImageViewBitmap(R.id.widget_img4, BitmapFactory.decodeFile(image4))
setViewVisibility(R.id.widget_img4, View.VISIBLE)
} else {
setViewVisibility(R.id.widget_img4, View.GONE)
}

// Nothing in your watchlist yet
if (image1 != null || image2 != null || image3 != null || image4 != null) {
setViewVisibility(R.id.widget_nothing, View.GONE)
} else {
setViewVisibility(R.id.widget_nothing, View.VISIBLE)
setTextViewText(R.id.widget_nothing, "Nothing in your watchlist yet")
}
}

appWidgetManager.updateAppWidget(widgetId, views)
}
}
}
5 changes: 5 additions & 0 deletions android/app/src/main/res/drawable/home_widget_background.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="?android:attr/colorBackground"/>
<corners android:radius="16dp"/>
</shape>
47 changes: 47 additions & 0 deletions android/app/src/main/res/layout/home_widget_layout.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:orientation="horizontal"
android:background="@drawable/home_widget_background"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:padding="8dp"
android:id="@+id/home_widget_container">

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/widget_img1"
android:visibility="gone"/>

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/widget_img2"
android:visibility="gone"/>

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/widget_img3"
android:visibility="gone"/>

<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/widget_img4"
android:visibility="gone"/>

<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:text="Nothing in your watchlist yet"
android:gravity="center"
android:id="@+id/widget_nothing"/>
</LinearLayout>
9 changes: 9 additions & 0 deletions android/app/src/main/res/xml/home_widget_provider.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="80dp"
android:minHeight="40dp"
android:updatePeriodMillis="1800000"
android:initialLayout="@layout/home_widget_layout"
android:resizeMode="horizontal"
android:widgetCategory="home_screen">
</appwidget-provider>
7 changes: 4 additions & 3 deletions lib/controllers/animes/missing_anime_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import 'package:jais/widgets/animes/missing_anime_widget.dart';

class MissingAnimeController extends DataController<MissingAnime,
MissingAnimeLoaderWidget, MissingAnimeWidget> with AbstractFilter {
MissingAnimeController({required super.notifyListenersCallback})
: super(
limit: 12,
MissingAnimeController({
required super.notifyListenersCallback,
super.limit = 12,
}) : super(
loadingWidget: const MissingAnimeLoaderWidget(),
fromJson: (json) => MissingAnime.fromJson(json),
toWidget: (anime) => MissingAnimeWidget(missingAnime: anime),
Expand Down
5 changes: 5 additions & 0 deletions lib/controllers/app_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:jais/controllers/datas/collection_data_controller.dart';
import 'package:jais/controllers/filter_controller.dart';
import 'package:jais/controllers/home_widget_controller.dart';
import 'package:jais/controllers/url_controller.dart';
import 'package:jais/firebase_options.dart';
import 'package:jais/utils.dart';
Expand All @@ -13,6 +14,8 @@ class AppController with ChangeNotifier {
CollectionDataController('animeWatchlist');
static final CollectionDataController seen =
CollectionDataController('episodesSeen');
static final HomeWidgetController homeWidgetController =
HomeWidgetController();

bool _inProgress = true;
bool _hasInternet = false;
Expand All @@ -23,6 +26,7 @@ class AppController with ChangeNotifier {

AppController() {
checkInternetConnection();
homeWidgetController.init();
}

Future<void> checkInternetConnection() async {
Expand All @@ -48,5 +52,6 @@ class AppController with ChangeNotifier {

await FirebaseMessaging.instance.requestPermission();
await FirebaseMessaging.instance.subscribeToTopic('all');
await homeWidgetController.notify();
}
}
4 changes: 2 additions & 2 deletions lib/controllers/episode_tab_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ class EpisodeTabController with ChangeNotifier {
episodeController.reset();
notify();

missingAnimeController.load().whenComplete(() => notify());
episodeController.load().whenComplete(() => notify());
missingAnimeController.load().whenComplete(notify);
episodeController.load().whenComplete(notify);
}

void notify() {
Expand Down
76 changes: 76 additions & 0 deletions lib/controllers/home_widget_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:home_widget/home_widget.dart';
import 'package:jais/controllers/animes/missing_anime_controller.dart';
import 'package:jais/controllers/logger.dart';
import 'package:jais/models/anime.dart';
import 'package:jais/utils.dart';
import 'package:jais/widgets/animes/anime_image.dart';
import 'package:jais/widgets/animes/missing_anime_widget.dart';

class HomeWidgetController {
late MissingAnimeController missingAnimeController;
static const int limit = 4;

void init() {
missingAnimeController =
MissingAnimeController(limit: 4, notifyListenersCallback: () {});
}

Future<void> notify() async {
missingAnimeController.reset();
await missingAnimeController.load();

try {
final List<AnimeImage> animeImages = [];

for (int i = 0; i < limit; i++) {
final MissingAnimeWidget? missingAnimeWidget =
missingAnimeController.list.elementAtOrNull(i)
as MissingAnimeWidget?;

if (missingAnimeWidget == null) {
break;
}

final Anime anime = missingAnimeWidget.missingAnime.anime;

animeImages.add(
AnimeImage(
anime: anime,
width: Const.missingAnimeImageWith * 10,
height: Const.missingAnimeImageHeight * 10,
radius: 360,
),
);
}

info(
'EpisodeTabController',
'updateHomeWidget() - ${animeImages.length} images',
);

if (animeImages.isNotEmpty) {
await Future.wait([
for (final AnimeImage image in animeImages)
HomeWidget.renderFlutterWidget(
image,
logicalSize: const Size(
Const.missingAnimeImageWith * 10,
Const.missingAnimeImageHeight * 10,
),
key: 'image${animeImages.indexOf(image)}',
),
]);
} else {
await Future.wait([
for (int i = 0; i < limit; i++)
HomeWidget.saveWidgetData('image$i', null),
]);
}

await HomeWidget.updateWidget(name: 'HomeWidgetProvider');
} catch (exception, stackTrace) {
error('EpisodeTabController', '$exception', exception, stackTrace);
}
}
}
16 changes: 14 additions & 2 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:home_widget/home_widget.dart';
import 'package:jais/controllers/animes/anime_detail_controller.dart';
import 'package:jais/controllers/animes/anime_diary_controller.dart';
import 'package:jais/controllers/animes/anime_search_controller.dart';
Expand All @@ -24,11 +25,22 @@ Future<void> main() async {
);
}

class MyApp extends StatelessWidget {
class MyApp extends StatefulWidget {
const MyApp({super.key});

@override
State<StatefulWidget> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
static const Color _mainWhiteThemeColor = Color(0xFFa32d26);
static const Color _mainDarkThemeColor = Color(0xFFfde5c9);

const MyApp({super.key});
@override
void initState() {
super.initState();
HomeWidget.setAppGroupId('jais');
}

@override
Widget build(BuildContext context) {
Expand Down
1 change: 1 addition & 0 deletions lib/widgets/episodes/episode_see_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class _EpisodeSeeWidgetState extends State<EpisodeSeeWidget> {
}

setState(() {});
await AppController.homeWidgetController.notify();
},
child: Icon(
isWatched ? Icons.visibility : Icons.visibility_off,
Expand Down
3 changes: 3 additions & 0 deletions lib/widgets/watchlist_button.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:jais/controllers/app_controller.dart';

class WatchlistButton extends StatefulWidget {
final bool inWatchlist;
Expand Down Expand Up @@ -47,6 +48,8 @@ class _WatchlistButtonState extends State<WatchlistButton> {
setState(() {
_inWatchlist = !_inWatchlist;
});

await AppController.homeWidgetController.notify();
},
);
}
Expand Down
9 changes: 9 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,15 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.1"
home_widget:
dependency: "direct main"
description:
path: "."
ref: e02d386487bf47324fb7ca5112eccb345a26cf06
resolved-ref: e02d386487bf47324fb7ca5112eccb345a26cf06
url: "https://github.com/Ziedelth/flutter_home_widget.git"
source: git
version: "0.3.0"
html:
dependency: transitive
description:
Expand Down
6 changes: 5 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ dependencies:
flutter:
sdk: flutter
flutter_svg: ^2.0.7
home_widget:
git:
url: https://github.com/Ziedelth/flutter_home_widget.git
ref: e02d386487bf47324fb7ca5112eccb345a26cf06
http: ^0.13.6
intl: ^0.18.1
json_annotation: ^4.8.1
Expand All @@ -25,9 +29,9 @@ dependencies:

dev_dependencies:
build_runner: ^2.4.6
flutterando_metrics: ^5.7.6
flutter_launcher_icons: ^0.13.1
flutter_native_splash: ^2.3.2
flutterando_metrics: ^5.7.6
json_serializable: ^6.7.1
lint: ^2.1.2

Expand Down