~cytrogen/fluent-reader-mobile

02ba12cc1c79b796ce713f80743db5520a067d21 — Bruce Liu 3 years ago b39dfad
add multiple locales
M android/app/build.gradle => android/app/build.gradle +1 -1
@@ 32,7 32,7 @@ if (keystorePropertiesFile.exists()) {
}

android {
    compileSdkVersion 29
    compileSdkVersion 31

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'

M android/app/src/main/AndroidManifest.xml => android/app/src/main/AndroidManifest.xml +1 -1
@@ 9,7 9,7 @@
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <application
        android:name="io.flutter.app.FlutterApplication"
        android:name="${applicationName}"
        android:label="Fluent Reader"
        android:icon="@mipmap/ic_launcher"
        android:usesCleartextTraffic="true">

M android/build.gradle => android/build.gradle +2 -2
@@ 1,12 1,12 @@
buildscript {
    ext.kotlin_version = '1.3.50'
    ext.kotlin_version = '1.7.0'
    repositories {
        google()
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.0'
        classpath 'com.android.tools.build:gradle:7.0.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

M android/gradle/wrapper/gradle-wrapper.properties => android/gradle/wrapper/gradle-wrapper.properties +1 -1
@@ 3,4 3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip

M ios/Runner.xcodeproj/project.pbxproj => ios/Runner.xcodeproj/project.pbxproj +12 -0
@@ 50,6 50,10 @@
		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		E30184022867BA2B005DCAE6 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
		E30184032867BA70005DCAE6 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
		E30184042867BA7D005DCAE6 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/LaunchScreen.strings"; sourceTree = "<group>"; };
		E30184052867BAB1005DCAE6 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
		FA1C5CF9893C1976B07DEA5C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */



@@ 179,6 183,10 @@
				"zh-Hans",
				es,
				de,
				fr,
				hr,
				"pt-BR",
				uk,
			);
			mainGroup = 97C146E51CF9000F007C117D;
			productRefGroup = 97C146EF1CF9000F007C117D /* Products */;


@@ 302,6 310,10 @@
				3271990C25BBF9C8008EA00E /* zh-Hans */,
				324ACECE25C909AD00CD3591 /* es */,
				32AC73DE263AED42001F500B /* de */,
				E30184022867BA2B005DCAE6 /* fr */,
				E30184032867BA70005DCAE6 /* hr */,
				E30184042867BA7D005DCAE6 /* pt-BR */,
				E30184052867BAB1005DCAE6 /* uk */,
			);
			name = LaunchScreen.storyboard;
			sourceTree = "<group>";

A ios/Runner/fr.lproj/LaunchScreen.strings => ios/Runner/fr.lproj/LaunchScreen.strings +1 -0
@@ 0,0 1,1 @@


A ios/Runner/hr.lproj/LaunchScreen.strings => ios/Runner/hr.lproj/LaunchScreen.strings +1 -0
@@ 0,0 1,1 @@


A ios/Runner/pt-BR.lproj/LaunchScreen.strings => ios/Runner/pt-BR.lproj/LaunchScreen.strings +1 -0
@@ 0,0 1,1 @@


A ios/Runner/uk.lproj/LaunchScreen.strings => ios/Runner/uk.lproj/LaunchScreen.strings +1 -0
@@ 0,0 1,1 @@


R lib/l10n/intl_ptBR.arb => lib/l10n/intl_pt.arb +0 -0
R lib/l10n/intl_ua.arb => lib/l10n/intl_uk.arb +0 -0
M lib/main.dart => lib/main.dart +18 -9
@@ 42,8 42,9 @@ void main() async {
  SystemChannels.lifecycle.setMessageHandler((msg) {
    if (msg == AppLifecycleState.resumed.toString()) {
      if (Global.server != null) Global.server.restart();
      if (Global.globalModel.syncOnStart
        && DateTime.now().difference(Global.syncModel.lastSynced).inMinutes >= 10) {
      if (Global.globalModel.syncOnStart &&
          DateTime.now().difference(Global.syncModel.lastSynced).inMinutes >=
              10) {
        Global.syncModel.syncWithService();
      }
    }


@@ 67,7 68,8 @@ class MyApp extends StatelessWidget {
    "/settings/service/inoreader": (context) => InoreaderPage(),
    "/settings/service/greader": (context) => GReaderPage(),
    "/settings/service": (context) {
      var serviceType = SyncService.values[Store.sp.getInt(StoreKeys.SYNC_SERVICE) ?? 0];
      var serviceType =
          SyncService.values[Store.sp.getInt(StoreKeys.SYNC_SERVICE) ?? 0];
      switch (serviceType) {
        case SyncService.None:
          break;


@@ 114,12 116,19 @@ class MyApp extends StatelessWidget {
            const Locale("en"),
            const Locale("es"),
            const Locale("zh"),
            const Locale("fr"),
            const Locale("uk"),
            const Locale("hr"),
            const Locale("pt"),
          ],
          localeResolutionCallback: (_locale, supportedLocales) {
            _locale = Locale(_locale.languageCode);
            if (globalModel.locale != null) return globalModel.locale;
            else if (supportedLocales.contains(_locale)) return _locale;
            else return Locale("en");
            if (globalModel.locale != null)
              return globalModel.locale;
            else if (supportedLocales.contains(_locale))
              return _locale;
            else
              return Locale("en");
          },
          theme: CupertinoThemeData(
            primaryColor: CupertinoColors.systemBlue,


@@ 133,9 142,9 @@ class MyApp extends StatelessWidget {
            final mediaQueryData = MediaQuery.of(context);
            if (Global.globalModel.textScale == null) return child;
            return MediaQuery(
              data: mediaQueryData.copyWith(textScaleFactor: Global.globalModel.textScale),
              child: child
            );
                data: mediaQueryData.copyWith(
                    textScaleFactor: Global.globalModel.textScale),
                child: child);
          },
        ),
      ),

M lib/pages/settings/general_page.dart => lib/pages/settings/general_page.dart +143 -125
@@ 18,137 18,155 @@ class _GeneralPageState extends State<GeneralPage> {
  double textScale;

  void _clearCache() async {
    setState(() { _clearingCache = true; });
    setState(() {
      _clearingCache = true;
    });
    await DefaultCacheManager().emptyCache();
    if (!mounted) return;
    setState(() { _clearingCache = false; });
    setState(() {
      _clearingCache = false;
    });
  }

  @override
  Widget build(BuildContext context) => CupertinoPageScaffold(
    backgroundColor: MyColors.background,
    navigationBar: CupertinoNavigationBar(
      middle: Text(S.of(context).general),
    ),
    child: Consumer<GlobalModel>(
      builder: (context, globalModel, child) {
        final useSystemTextScale = globalModel.textScale == null;
        final textScaleItems = ListTileGroup([
          MyListTile(
            title: Text(S.of(context).followSystem),
            trailing: CupertinoSwitch(
              value: useSystemTextScale,
              onChanged: (v) {
                textScale = null;
                globalModel.textScale = v ? null : 1;
        backgroundColor: MyColors.background,
        navigationBar: CupertinoNavigationBar(
          middle: Text(S.of(context).general),
        ),
        child: Consumer<GlobalModel>(
          builder: (context, globalModel, child) {
            final useSystemTextScale = globalModel.textScale == null;
            final textScaleItems = ListTileGroup([
              MyListTile(
                title: Text(S.of(context).followSystem),
                trailing: CupertinoSwitch(
                  value: useSystemTextScale,
                  onChanged: (v) {
                    textScale = null;
                    globalModel.textScale = v ? null : 1;
                  },
                ),
                trailingChevron: false,
                withDivider: !useSystemTextScale,
              ),
              if (!useSystemTextScale)
                MyListTile(
                  title: Expanded(
                      child: CupertinoSlider(
                    min: 0.5,
                    max: 1.5,
                    divisions: 8,
                    value: textScale ?? globalModel.textScale,
                    onChanged: (v) {
                      setState(() {
                        textScale = v;
                      });
                    },
                    onChangeEnd: (v) {
                      textScale = null;
                      globalModel.textScale = v;
                    },
                  )),
                  trailingChevron: false,
                  withDivider: false,
                ),
            ], title: S.of(context).fontSize);
            final syncItems = ListTileGroup([
              MyListTile(
                title: Text(S.of(context).syncOnStart),
                trailing: CupertinoSwitch(
                  value: globalModel.syncOnStart,
                  onChanged: (v) {
                    globalModel.syncOnStart = v;
                    setState(() {});
                  },
                ),
                trailingChevron: false,
              ),
              MyListTile(
                title: Text(S.of(context).inAppBrowser),
                trailing: CupertinoSwitch(
                  value: globalModel.inAppBrowser,
                  onChanged: (v) {
                    globalModel.inAppBrowser = v;
                    setState(() {});
                  },
                ),
                trailingChevron: false,
                withDivider: false,
              ),
            ], title: S.of(context).preferences);
            final storageItems = ListTileGroup([
              MyListTile(
                title: Text(S.of(context).clearCache),
                onTap: _clearingCache ? null : _clearCache,
                trailing: _clearingCache ? CupertinoActivityIndicator() : null,
                trailingChevron: !_clearingCache,
              ),
              MyListTile(
                title: Text(S.of(context).autoDelete),
                trailing:
                    Text(S.of(context).daysAgo(globalModel.keepItemsDays)),
                trailingChevron: false,
                withDivider: false,
              ),
              MyListTile(
                title: Expanded(
                    child: CupertinoSlider(
                  min: 1,
                  max: 4,
                  divisions: 3,
                  value: (globalModel.keepItemsDays ~/ 7).toDouble(),
                  onChanged: (v) {
                    globalModel.keepItemsDays = (v * 7).toInt();
                    setState(() {});
                  },
                )),
                trailingChevron: false,
                withDivider: false,
              ),
            ], title: S.of(context).storage);
            final themeItems = ListTileGroup.fromOptions(
              [
                Tuple2(S.of(context).followSystem, ThemeSetting.Default),
                Tuple2(S.of(context).light, ThemeSetting.Light),
                Tuple2(S.of(context).dark, ThemeSetting.Dark),
              ],
              globalModel.theme,
              (t) {
                globalModel.theme = t;
              },
            ),
            trailingChevron: false,
            withDivider: !useSystemTextScale,
          ),
          if (!useSystemTextScale) MyListTile(
            title: Expanded(child: CupertinoSlider(
              min: 0.5,
              max: 1.5,
              divisions: 8,
              value: textScale ?? globalModel.textScale,
              onChanged: (v) {
                setState(() { textScale = v; });
              title: S.of(context).theme,
            );
            final localeItems = ListTileGroup.fromOptions(
              [
                Tuple2(S.of(context).followSystem, null),
                const Tuple2("Deutsch", Locale("de")),
                const Tuple2("English", Locale("en")),
                const Tuple2("Español", Locale("es")),
                const Tuple2("Français", Locale("fr")),
                const Tuple2("hrvatski", Locale("hr")),
                const Tuple2("Português do Brasil", Locale("pt")),
                const Tuple2("Українська", Locale("uk")),
                const Tuple2("中文(简体)", Locale("zh")),
              ],
              globalModel.locale,
              (l) {
                globalModel.locale = l;
              },
              onChangeEnd: (v) {
                textScale = null;
                globalModel.textScale = v;
              },
            )),
            trailingChevron: false,
            withDivider: false,
          ),
        ], title: S.of(context).fontSize);
        final syncItems = ListTileGroup([
          MyListTile(
            title: Text(S.of(context).syncOnStart),
            trailing: CupertinoSwitch(
              value: globalModel.syncOnStart,
              onChanged: (v) {
                globalModel.syncOnStart = v;
                setState(() {});
              },
            ),
            trailingChevron: false,
          ),
          MyListTile(
            title: Text(S.of(context).inAppBrowser),
            trailing: CupertinoSwitch(
              value: globalModel.inAppBrowser,
              onChanged: (v) {
                globalModel.inAppBrowser = v;
                setState(() {});
              },
            ),
            trailingChevron: false,
            withDivider: false,
          ),
        ], title: S.of(context).preferences);
        final storageItems = ListTileGroup([
          MyListTile(
            title: Text(S.of(context).clearCache),
            onTap: _clearingCache ? null : _clearCache,
            trailing: _clearingCache ? CupertinoActivityIndicator() : null,
            trailingChevron: !_clearingCache,
          ),
          MyListTile(
            title: Text(S.of(context).autoDelete),
            trailing: Text(S.of(context).daysAgo(globalModel.keepItemsDays)),
            trailingChevron: false,
            withDivider: false,
          ),
          MyListTile(
            title: Expanded(child: CupertinoSlider(
              min: 1,
              max: 4,
              divisions: 3,
              value: (globalModel.keepItemsDays ~/ 7).toDouble(),
              onChanged: (v) { 
                globalModel.keepItemsDays = (v * 7).toInt(); 
                setState(() { });
              },
            )),
            trailingChevron: false,
            withDivider: false,
          ),
        ], title: S.of(context).storage);
        final themeItems = ListTileGroup.fromOptions(
          [
            Tuple2(S.of(context).followSystem, ThemeSetting.Default),
            Tuple2(S.of(context).light, ThemeSetting.Light),
            Tuple2(S.of(context).dark, ThemeSetting.Dark),
          ],
          globalModel.theme,
          (t) { globalModel.theme = t; },
          title: S.of(context).theme,
        );
        final localeItems = ListTileGroup.fromOptions(
          [
            Tuple2(S.of(context).followSystem, null),
            const Tuple2("Deutsch", Locale("de")),
            const Tuple2("English", Locale("en")),
            const Tuple2("Español", Locale("es")),
            const Tuple2("中文(简体)", Locale("zh")),
          ],
          globalModel.locale,
          (l) { globalModel.locale = l; },
          title: S.of(context).language,
        );
        return ListView(
          children: [
            syncItems,
            textScaleItems,
            storageItems,
            themeItems,
            localeItems,
          ],
        );
      },
    ),
  );
              title: S.of(context).language,
            );
            return ListView(
              children: [
                syncItems,
                textScaleItems,
                storageItems,
                themeItems,
                localeItems,
              ],
            );
          },
        ),
      );
}