Compare commits
122 Commits
Author | SHA1 | Date | |
---|---|---|---|
ed48d95375 | |||
1eaded5694 | |||
70bb78afcb | |||
df2d2478d5 | |||
d5ae60327d | |||
615a092c1e | |||
5a7699d866 | |||
56a9bab3f2 | |||
e9bbf46b4f | |||
10f503a6c0 | |||
582f3156b2 | |||
90fb45146f | |||
c19c54e762 | |||
70e5a84b63 | |||
3a51fa83f2 | |||
cb90751330 | |||
835ed7e841 | |||
125ccd2dd1 | |||
5b991c4287 | |||
7dc3618afe | |||
eef4691814 | |||
9f71701845 | |||
d27203b041 | |||
4f280ec4c9 | |||
72cb2737ca | |||
215203bd16 | |||
3e320faece | |||
1049568246 | |||
71aa42118d | |||
4f21d3e6bd | |||
96d0fe9e5e | |||
69eee3e278 | |||
36bcd996c0 | |||
5fc39d8b8b | |||
5dce7787e1 | |||
8888dde792 | |||
6c8fc4cf87 | |||
ae9cc109db | |||
c8976ed17b | |||
ff7e115418 | |||
0310507c96 | |||
58c646e232 | |||
08328e2ca1 | |||
86b7228ffd | |||
e103c88ca6 | |||
94323a04e0 | |||
4776c375a1 | |||
1f4e6cf41c | |||
be6ed35888 | |||
b2ea50cea6 | |||
109b9287cf | |||
939d55ef0d | |||
3ee60e1a44 | |||
6fe567fa02 | |||
bc2d4f32c9 | |||
91290e9743 | |||
934f184b6f | |||
dbd48eae99 | |||
279007191b | |||
b3fdc20fc5 | |||
3fbf5d4eea | |||
332ffbb773 | |||
346a6c709e | |||
d4fe042245 | |||
b82c4a1777 | |||
7e0d1f0f1d | |||
f405a10c2e | |||
edbad79cd3 | |||
c9d8b2950a | |||
f2bc48f980 | |||
d56697c57c | |||
320ec41aae | |||
d85b3535d5 | |||
f8cd1cbba0 | |||
817ec208d6 | |||
554a165789 | |||
0c680370ef | |||
59541d2fcc | |||
32083c3564 | |||
258dbc4b8b | |||
6c8047ebac | |||
00a0135867 | |||
1db7be7a2c | |||
ff400f9c40 | |||
f03b45a98a | |||
cbe5bba986 | |||
268f4054a3 | |||
988c5d9881 | |||
e748e2f818 | |||
1b0a0dbda9 | |||
64d68389ba | |||
381c99b353 | |||
39ee3137f8 | |||
0d76be8634 | |||
9986f72e11 | |||
ef557e7b84 | |||
ec065c0122 | |||
2960c6e59e | |||
92dac6b932 | |||
20365393a3 | |||
8d238744c7 | |||
e33ff417fb | |||
d8922c2641 | |||
c6e0461857 | |||
30ca356dc8 | |||
7d11398e6d | |||
a4f52284ef | |||
c7d1a42d5a | |||
f83fd66bcc | |||
c2ec3647e2 | |||
ba63852b7d | |||
438041183c | |||
114540edd7 | |||
588b3e9508 | |||
2f0376f8f8 | |||
ab4051c018 | |||
c230c21218 | |||
c24e12237e | |||
e15dcba93b | |||
1362b93a74 | |||
ac18793f98 | |||
e52f65c773 |
3
.github/workflows/commit_check.yml
vendored
@ -9,13 +9,14 @@ on:
|
||||
jobs:
|
||||
releases:
|
||||
name: Check commit
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: checkout all the submodules
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- run: submodules/flutter/bin/flutter doctor
|
||||
- run: submodules/flutter/bin/flutter pub get
|
||||
- run: submodules/flutter/bin/dart format --set-exit-if-changed lib test integration_test
|
||||
|
1
.github/workflows/publish_ios.yml
vendored
@ -23,6 +23,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
fetch-depth: 0
|
||||
- run: submodules/flutter/bin/flutter doctor
|
||||
- run: submodules/flutter/bin/flutter pub get
|
||||
- run: submodules/flutter/bin/dart format --set-exit-if-changed lib test integration_test
|
||||
|
33
README.md
@ -1,7 +1,7 @@
|
||||
|
||||
# <img width="64" src="https://user-images.githubusercontent.com/7277662/167775086-0b234f28-dee4-44f6-aae4-14a28ed4bbb6.png"> Hacki for Hacker News
|
||||
|
||||
A [Hacker News](https://news.ycombinator.com/) client made with Flutter that is just enough.
|
||||
A [Hacker News](https://news.ycombinator.com/) client built with Flutter.
|
||||
|
||||
[](https://apps.apple.com/us/app/hacki/id1602043763?platform=iphone)
|
||||
[](https://f-droid.org/en/packages/com.jiaqifeng.hacki/)
|
||||
@ -29,27 +29,26 @@ Features:
|
||||
- Download stories and comments for offline reading.
|
||||
- Pick up where you left off.
|
||||
- Synced favorites and pins across devices. (iOS only)
|
||||
- Export or import your favorites.
|
||||
- Launch from system share sheet.
|
||||
- And more...
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img width="200" alt="01" src="assets/screenshots/01.png">
|
||||
<img width="200" alt="02" src="assets/screenshots/02.png">
|
||||
<img width="200" alt="03" src="assets/screenshots/03.png">
|
||||
<img width="200" alt="04" src="assets/screenshots/04.png">
|
||||
<img width="200" alt="05" src="assets/screenshots/05.png">
|
||||
<img width="200" alt="06" src="assets/screenshots/06.png">
|
||||
<img width="200" alt="07" src="assets/screenshots/07.png">
|
||||
<img width="200" alt="08" src="assets/screenshots/08.png">
|
||||
<img width="200" alt="09" src="assets/screenshots/09.png">
|
||||
<img width="200" alt="10" src="assets/screenshots/10.png">
|
||||
<img width="200" alt="11" src="assets/screenshots/11.png">
|
||||
<img width="200" alt="12" src="assets/screenshots/12.png">
|
||||
<img width="400" alt="01" src="assets/screenshots/light-1.png">
|
||||
<img width="400" alt="06" src="assets/screenshots/dark-1.png">
|
||||
<img width="400" alt="02" src="assets/screenshots/light-2.png">
|
||||
<img width="400" alt="07" src="assets/screenshots/dark-2.png">
|
||||
<img width="400" alt="03" src="assets/screenshots/light-3.png">
|
||||
<img width="400" alt="08" src="assets/screenshots/dark-3.png">
|
||||
<img width="400" alt="04" src="assets/screenshots/light-4.png">
|
||||
<img width="400" alt="09" src="assets/screenshots/dark-4.png">
|
||||
<img width="400" alt="05" src="assets/screenshots/light-5.png">
|
||||
<img width="400" alt="10" src="assets/screenshots/dark-5.png">
|
||||
|
||||
<img width="400" alt="ipad-01" src="assets/screenshots/ipad-01.png">
|
||||
<img width="400" alt="ipad-02" src="assets/screenshots/ipad-02.png">
|
||||
<img width="400" alt="ipad-03" src="assets/screenshots/ipad-03.png">
|
||||
<img width="400" alt="ipad-04" src="assets/screenshots/ipad-04.png">
|
||||
<img width="400" alt="ipad-01" src="assets/screenshots/tablet-light-1.png">
|
||||
<img width="400" alt="ipad-02" src="assets/screenshots/tablet-dark-1.png">
|
||||
<img width="400" alt="ipad-03" src="assets/screenshots/tablet-light-2.png">
|
||||
<img width="400" alt="ipad-04" src="assets/screenshots/tablet-dark-2.png">
|
||||
</p>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
include: package:very_good_analysis/analysis_options.3.1.0.yaml
|
||||
include: package:very_good_analysis/analysis_options.5.0.0.yaml
|
||||
linter:
|
||||
rules:
|
||||
parameter_assignments: false
|
||||
|
@ -50,7 +50,7 @@ android {
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.jiaqifeng.hacki"
|
||||
minSdkVersion 26
|
||||
minSdkVersion 25
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
@ -13,6 +13,9 @@
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<data android:mimeType="*/*" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.support.customtabs.action.CustomTabsService" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
@ -20,7 +23,8 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:usesCleartextTraffic="true">
|
||||
android:usesCleartextTraffic="true"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
|
@ -2,4 +2,5 @@
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@mipmap/ic_launcher_adaptive_back"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_adaptive_fore"/>
|
||||
<monochrome android:drawable="@mipmap/ic_launcher_monochrome"/>
|
||||
</adaptive-icon>
|
BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png
Normal file
After Width: | Height: | Size: 940 B |
BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.4 KiB |
@ -24,6 +24,6 @@ subprojects {
|
||||
project.evaluationDependsOn(':app')
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
tasks.register("clean", Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
BIN
assets/fonts/exo_2/Exo2-Bold.ttf
Normal file
BIN
assets/fonts/exo_2/Exo2-Regular.ttf
Normal file
BIN
assets/fonts/noto_serif/NotoSerif-Bold.ttf
Normal file
BIN
assets/fonts/noto_serif/NotoSerif-Regular.ttf
Normal file
BIN
assets/hacki-github.png
Normal file
After Width: | Height: | Size: 419 KiB |
BIN
assets/hacki-github.xcf
Normal file
BIN
assets/hacki.xcf
Normal file
Before Width: | Height: | Size: 548 KiB After Width: | Height: | Size: 333 KiB |
Before Width: | Height: | Size: 571 KiB After Width: | Height: | Size: 341 KiB |
Before Width: | Height: | Size: 592 KiB After Width: | Height: | Size: 359 KiB |
BIN
assets/screenshots/dark-1.png
Normal file
After Width: | Height: | Size: 1003 KiB |
BIN
assets/screenshots/dark-2.png
Normal file
After Width: | Height: | Size: 912 KiB |
BIN
assets/screenshots/dark-3.png
Normal file
After Width: | Height: | Size: 252 KiB |
BIN
assets/screenshots/dark-4.png
Normal file
After Width: | Height: | Size: 734 KiB |
BIN
assets/screenshots/dark-5.png
Normal file
After Width: | Height: | Size: 1.1 MiB |
BIN
assets/screenshots/hacki-1.png
Normal file
After Width: | Height: | Size: 890 KiB |
BIN
assets/screenshots/hacki-2.png
Normal file
After Width: | Height: | Size: 873 KiB |
BIN
assets/screenshots/hacki-3.png
Normal file
After Width: | Height: | Size: 770 KiB |
BIN
assets/screenshots/hacki-4.png
Normal file
After Width: | Height: | Size: 517 KiB |
BIN
assets/screenshots/light-1.png
Normal file
After Width: | Height: | Size: 1.3 MiB |
BIN
assets/screenshots/light-2.png
Normal file
After Width: | Height: | Size: 893 KiB |
BIN
assets/screenshots/light-3.png
Normal file
After Width: | Height: | Size: 460 KiB |
BIN
assets/screenshots/light-4.png
Normal file
After Width: | Height: | Size: 712 KiB |
BIN
assets/screenshots/light-5.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
assets/screenshots/tablet-dark-1.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/screenshots/tablet-dark-2.png
Normal file
After Width: | Height: | Size: 1.5 MiB |
BIN
assets/screenshots/tablet-light-1.png
Normal file
After Width: | Height: | Size: 1.9 MiB |
BIN
assets/screenshots/tablet-light-2.png
Normal file
After Width: | Height: | Size: 1.7 MiB |
BIN
assets/tablet-hacki.xcf
Normal file
30
components/in_app_review/.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
39
components/in_app_review/.metadata
Normal file
@ -0,0 +1,39 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled.
|
||||
|
||||
version:
|
||||
revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
channel: stable
|
||||
|
||||
project_type: plugin
|
||||
|
||||
# Tracks metadata for the flutter migrate command
|
||||
migration:
|
||||
platforms:
|
||||
- platform: root
|
||||
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
- platform: android
|
||||
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
- platform: ios
|
||||
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
- platform: macos
|
||||
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
- platform: windows
|
||||
create_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
base_revision: e3c29ec00c9c825c891d75054c63fcc46454dca1
|
||||
|
||||
# User provided section
|
||||
|
||||
# List of Local paths (relative to this file) that should be
|
||||
# ignored by the migrate tool.
|
||||
#
|
||||
# Files that are not part of the templates will be ignored by default.
|
||||
unmanaged_files:
|
||||
- 'lib/main.dart'
|
||||
- 'ios/Runner.xcodeproj/project.pbxproj'
|
102
components/in_app_review/CHANGELOG.md
Normal file
@ -0,0 +1,102 @@
|
||||
# [2.0.6]
|
||||
- Update Android Play Core dependency to Play Review 2.0.1.
|
||||
|
||||
# [2.0.5]
|
||||
|
||||
- Migrate Android Play Core dependency to Play Review 2.0.0.
|
||||
- Recreate the example app.
|
||||
- Update in_app_review_platform_interface to 2.0.4
|
||||
|
||||
# [2.0.4]
|
||||
|
||||
- Migrate maven repository from jcenter to mavenCentral
|
||||
- `isAvailable()` now returns `false` on web.
|
||||
|
||||
# [2.0.3]
|
||||
|
||||
- Fix iOS no-scene exception. ([#41](https://github.com/britannio/in_app_review/issues/41))
|
||||
# [2.0.2]
|
||||
|
||||
- Replace iOS Swift code with Objective-C to add compatibility with Objective-C Flutter apps.
|
||||
|
||||
# [2.0.1]
|
||||
|
||||
- Fix rare null pointer exception on Android
|
||||
- Fix MissingPluginException on MacOS
|
||||
- Bump the minimum Dart SDK version from `2.12.0-0` to `2.12.0`.
|
||||
- Bump the minimum Flutter version to `2.0.0`.
|
||||
- Update in_app_review_platform_interface to 2.0.2
|
||||
|
||||
# [2.0.0]
|
||||
|
||||
- Migrate to null safety.
|
||||
|
||||
# [1.0.4]
|
||||
|
||||
- Update in_app_review_platform_interface to 1.0.5
|
||||
- Remove dependency on `package_info`.
|
||||
- Handle `openStoreListing()` with native code for Android, iOS and MacOS.
|
||||
|
||||
# [1.0.3]
|
||||
|
||||
- Update in_app_review_platform_interface to 1.0.4
|
||||
- Update android compileSdkVersion to 29.
|
||||
- Lower dependency version constraints.
|
||||
|
||||
# [1.0.2]
|
||||
|
||||
- Update in_app_review_platform_interface to 1.0.3
|
||||
- Open the App Store directly instead of via the Safari View Controller.
|
||||
- Add automated tests.
|
||||
- Improve docs.
|
||||
|
||||
# [1.0.1+1]
|
||||
|
||||
- Update in_app_review_platform_interface to 1.0.2
|
||||
|
||||
# [1.0.0]
|
||||
|
||||
- Migrate to use `in_app_review_platform_interface`.
|
||||
- Add Windows support for `openStoreListing`.
|
||||
|
||||
# [0.2.1+1]
|
||||
|
||||
- Improve iOS testing docs.
|
||||
|
||||
# [0.2.1]
|
||||
|
||||
- Update dependencies.
|
||||
- Android Play Core Library V1.8.2 release notes:
|
||||
- Fixed UI flickering in the In-App Review API
|
||||
|
||||
# [0.2.0+4]
|
||||
|
||||
- Remove deprecated API warning.
|
||||
- Update dependencies.
|
||||
|
||||
# [0.2.0+3]
|
||||
|
||||
- Instructions in the README have been improved along with the example.
|
||||
|
||||
# [0.2.0+2]
|
||||
|
||||
- Update changelog format
|
||||
|
||||
# [0.2.0+1]
|
||||
|
||||
- Update MacOS testing instructions
|
||||
|
||||
# [0.2.0] Breaking Change
|
||||
|
||||
- Add MacOS support
|
||||
- Rename `openStoreListing(iOSAppStoreId: '')` to `openStoreListing(appStoreId: '')`
|
||||
|
||||
# [0.1.0]
|
||||
|
||||
- Improve docs
|
||||
- Set Android minSdkVersion to 16
|
||||
- Refactor Android Plugin
|
||||
|
||||
# [0.0.1]
|
||||
|
||||
Initial release
|
21
components/in_app_review/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Britannio Jarrett
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
0
components/in_app_review/analysis_options.yaml
Normal file
9
components/in_app_review/android/.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.cxx
|
49
components/in_app_review/android/build.gradle
Normal file
@ -0,0 +1,49 @@
|
||||
group 'dev.britannio.in_app_review'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.10'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
}
|
||||
dependencies {
|
||||
|
||||
}
|
||||
}
|
1
components/in_app_review/android/settings.gradle
Normal file
@ -0,0 +1 @@
|
||||
rootProject.name = 'in_app_review'
|
@ -0,0 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="dev.britannio.in_app_review">
|
||||
</manifest>
|
@ -0,0 +1,59 @@
|
||||
package dev.britannio.in_app_review;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
|
||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||
import io.flutter.plugin.common.MethodChannel.Result;
|
||||
|
||||
/**
|
||||
* InAppReviewPlugin
|
||||
*/
|
||||
public class InAppReviewPlugin implements FlutterPlugin, MethodCallHandler {
|
||||
/// The MethodChannel that will the communication between Flutter and native Android
|
||||
///
|
||||
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
|
||||
/// when the Flutter Engine is detached from the Activity
|
||||
private MethodChannel channel;
|
||||
|
||||
|
||||
private final String TAG = "InAppReviewPlugin";
|
||||
|
||||
@Override
|
||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
||||
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "dev.britannio.in_app_review");
|
||||
channel.setMethodCallHandler(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
|
||||
Log.i(TAG, "onMethodCall: " + call.method);
|
||||
switch (call.method) {
|
||||
case "isAvailable":
|
||||
case "requestReview":
|
||||
case "openStoreListing":
|
||||
default:
|
||||
result.notImplemented();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
|
||||
channel.setMethodCallHandler(null);
|
||||
}
|
||||
}
|
38
components/in_app_review/ios/.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
.idea/
|
||||
.vagrant/
|
||||
.sconsign.dblite
|
||||
.svn/
|
||||
|
||||
.DS_Store
|
||||
*.swp
|
||||
profile
|
||||
|
||||
DerivedData/
|
||||
build/
|
||||
GeneratedPluginRegistrant.h
|
||||
GeneratedPluginRegistrant.m
|
||||
|
||||
.generated/
|
||||
|
||||
*.pbxuser
|
||||
*.mode1v3
|
||||
*.mode2v3
|
||||
*.perspectivev3
|
||||
|
||||
!default.pbxuser
|
||||
!default.mode1v3
|
||||
!default.mode2v3
|
||||
!default.perspectivev3
|
||||
|
||||
xcuserdata
|
||||
|
||||
*.moved-aside
|
||||
|
||||
*.pyc
|
||||
*sync/
|
||||
Icon?
|
||||
.tags*
|
||||
|
||||
/Flutter/Generated.xcconfig
|
||||
/Flutter/ephemeral/
|
||||
/Flutter/flutter_export_environment.sh
|
0
components/in_app_review/ios/Assets/.gitkeep
Normal file
4
components/in_app_review/ios/Classes/InAppReviewPlugin.h
Normal file
@ -0,0 +1,4 @@
|
||||
#import <Flutter/Flutter.h>
|
||||
|
||||
@interface InAppReviewPlugin : NSObject<FlutterPlugin>
|
||||
@end
|
107
components/in_app_review/ios/Classes/InAppReviewPlugin.m
Normal file
@ -0,0 +1,107 @@
|
||||
#import "InAppReviewPlugin.h"
|
||||
|
||||
@import StoreKit;
|
||||
@implementation InAppReviewPlugin
|
||||
|
||||
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
|
||||
|
||||
FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"dev.britannio.in_app_review" binaryMessenger:[registrar messenger]];
|
||||
|
||||
InAppReviewPlugin* instance = [[InAppReviewPlugin alloc] init];
|
||||
[registrar addMethodCallDelegate:instance channel:channel];
|
||||
}
|
||||
|
||||
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
|
||||
|
||||
[self logMessage:@"handle" details:call.method];
|
||||
|
||||
if ([call.method isEqual:@"requestReview"]) {
|
||||
[self requestReview:result];
|
||||
} else if ([call.method isEqual:@"isAvailable"]) {
|
||||
[self isAvailable:result];
|
||||
} else if ([call.method isEqual:@"openStoreListing"]) {
|
||||
[self openStoreListingWithStoreId:call.arguments result:result];
|
||||
} else {
|
||||
[self logMessage:@"method not implemented"];
|
||||
result(FlutterMethodNotImplemented);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) requestReview:(FlutterResult)result {
|
||||
if (@available(iOS 14, *)) {
|
||||
[self logMessage:@"iOS 14+"];
|
||||
UIWindowScene *scene = [self findActiveScene];
|
||||
[SKStoreReviewController requestReviewInScene:scene];
|
||||
result(nil);
|
||||
} else if (@available(iOS 10.3, *)) {
|
||||
[self logMessage:@"iOS 10.3+"];
|
||||
[SKStoreReviewController requestReview];
|
||||
result(nil);
|
||||
} else {
|
||||
result([FlutterError errorWithCode:@"unavailable"
|
||||
message:@"In-App Review unavailable"
|
||||
details:nil]);
|
||||
}
|
||||
}
|
||||
|
||||
- (UIWindowScene *) findActiveScene API_AVAILABLE(ios(13.0)){
|
||||
for (UIWindowScene *scene in UIApplication.sharedApplication.connectedScenes) {
|
||||
|
||||
if (scene.activationState == UISceneActivationStateForegroundActive) {
|
||||
return scene;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void) isAvailable:(FlutterResult)result {
|
||||
if (@available(iOS 10.3, *)) {
|
||||
[self logMessage:@"available"];
|
||||
result(@YES);
|
||||
} else {
|
||||
[self logMessage:@"unavailable"];
|
||||
result(@NO);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) openStoreListingWithStoreId:(NSString *)storeId result:(FlutterResult)result {
|
||||
|
||||
if (!storeId) {
|
||||
result([FlutterError errorWithCode:@"no-store-id"
|
||||
message:@"Your store id must be passed as the method channel's argument"
|
||||
details:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"https://apps.apple.com/app/id%@?action=write-review", storeId]];
|
||||
|
||||
if (!url) {
|
||||
result([FlutterError errorWithCode:@"url-construct-fail"
|
||||
message:@"Failed to construct url"
|
||||
details:nil]);
|
||||
return;
|
||||
}
|
||||
|
||||
UIApplication *app = [UIApplication sharedApplication];
|
||||
if (@available(iOS 10.0, *)) {
|
||||
[app openURL:url options:@{} completionHandler:nil];
|
||||
} else {
|
||||
[app openURL:url];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Logging Helpers
|
||||
|
||||
- (void) logMessage:(NSString *) message {
|
||||
NSLog(@"InAppReviewPlugin: %@", message);
|
||||
}
|
||||
|
||||
- (void) logMessage:(NSString *) message
|
||||
details:(NSString *) details {
|
||||
NSLog(@"InAppReviewPlugin: %@ %@", message, details);
|
||||
}
|
||||
|
||||
@end
|
23
components/in_app_review/ios/in_app_review.podspec
Normal file
@ -0,0 +1,23 @@
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint in_app_review.podspec` to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'in_app_review'
|
||||
s.version = '0.2.0'
|
||||
s.summary = 'Flutter plugin for showing the In-App Review/System Rating pop up.'
|
||||
s.description = <<-DESC
|
||||
Flutter plugin for showing the In-App Review/System Rating pop up..
|
||||
DESC
|
||||
s.homepage = 'http://example.com'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Britannio Jarrett' => 'britanniojarrett@gmail.com' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'Flutter'
|
||||
s.platform = :ios, '9.0'
|
||||
|
||||
# Flutter.framework does not contain a i386 slice.
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
||||
s.swift_version = '5.0'
|
||||
end
|
50
components/in_app_review/lib/in_app_review.dart
Normal file
@ -0,0 +1,50 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:in_app_review_platform_interface/in_app_review_platform_interface.dart';
|
||||
|
||||
class InAppReview {
|
||||
InAppReview._();
|
||||
|
||||
static final InAppReview instance = InAppReview._();
|
||||
|
||||
/// Checks if the device is able to show a review dialog.
|
||||
///
|
||||
/// On Android the Google Play Store must be installed and the device must be
|
||||
/// running **Android 5 Lollipop(API 21)** or higher.
|
||||
///
|
||||
/// iOS devices must be running **iOS version 10.3** or higher.
|
||||
///
|
||||
/// MacOS devices must be running **MacOS version 10.14** or higher
|
||||
Future<bool> isAvailable() => InAppReviewPlatform.instance.isAvailable();
|
||||
|
||||
/// Attempts to show the review dialog. It's recommended to first check if
|
||||
/// the device supports this feature via [isAvailable].
|
||||
///
|
||||
/// To improve the users experience, iOS and Android enforce limitations
|
||||
/// that might prevent this from working after a few tries. iOS & MacOS users
|
||||
/// can also disable this feature entirely in the App Store settings.
|
||||
///
|
||||
/// More info and guidance:
|
||||
/// https://developer.android.com/guide/playcore/in-app-review#when-to-request
|
||||
/// https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/ratings-and-reviews/
|
||||
/// https://developer.apple.com/design/human-interface-guidelines/macos/system-capabilities/ratings-and-reviews/
|
||||
Future<void> requestReview() => InAppReviewPlatform.instance.requestReview();
|
||||
|
||||
/// Opens the Play Store on Android, the App Store with a review
|
||||
/// screen on iOS & MacOS and the Microsoft Store on Windows.
|
||||
///
|
||||
/// [appStoreId] is required for iOS & MacOS.
|
||||
///
|
||||
/// [microsoftStoreId] is required for Windows.
|
||||
Future<void> openStoreListing({
|
||||
/// Required for iOS & MacOS.
|
||||
String? appStoreId,
|
||||
|
||||
/// Required for Windows.
|
||||
String? microsoftStoreId,
|
||||
}) =>
|
||||
InAppReviewPlatform.instance.openStoreListing(
|
||||
appStoreId: appStoreId,
|
||||
microsoftStoreId: microsoftStoreId,
|
||||
);
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import StoreKit
|
||||
|
||||
public class InAppReviewPlugin: NSObject, FlutterPlugin {
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(name: "dev.britannio.in_app_review", binaryMessenger: registrar.messenger)
|
||||
let instance = InAppReviewPlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "requestReview":
|
||||
//App Store Review
|
||||
if #available(OSX 10.14, *) {
|
||||
SKStoreReviewController.requestReview()
|
||||
result(nil)
|
||||
} else {
|
||||
result(FlutterError(code: "unavailable", message: "In-App Review unavailable", details: nil))
|
||||
}
|
||||
case "isAvailable":
|
||||
if #available(OSX 10.14, *) {
|
||||
result(true)
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
case "openStoreListing":
|
||||
let storeId : String = call.arguments as! String;
|
||||
|
||||
guard let writeReviewURL = URL(string: "macappstore://apps.apple.com/app/id" + storeId + "?action=write-review")
|
||||
else {
|
||||
result(FlutterError(code: "url_construct_fail", message: "Failed to construct url", details: nil))
|
||||
return
|
||||
}
|
||||
NSWorkspace.shared.open(writeReviewURL)
|
||||
result(nil);
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
22
components/in_app_review/macos/in_app_review.podspec
Normal file
@ -0,0 +1,22 @@
|
||||
#
|
||||
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
|
||||
# Run `pod lib lint in_app_review.podspec' to validate before publishing.
|
||||
#
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'in_app_review'
|
||||
s.version = '0.2.0'
|
||||
s.summary = 'Flutter plugin for showing the In-App Review/System Rating pop up.'
|
||||
s.description = <<-DESC
|
||||
Flutter plugin for showing the In-App Review/System Rating pop up.
|
||||
DESC
|
||||
s.homepage = 'https://github.com/britannio/in_app_review'
|
||||
s.license = { :file => '../LICENSE' }
|
||||
s.author = { 'Britannio Jarrett' => 'britanniojarrett@gmail.com' }
|
||||
s.source = { :path => '.' }
|
||||
s.source_files = 'Classes/**/*'
|
||||
s.dependency 'FlutterMacOS'
|
||||
|
||||
s.platform = :osx, '10.11'
|
||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
||||
s.swift_version = '5.0'
|
||||
end
|
46
components/in_app_review/pubspec.yaml
Normal file
@ -0,0 +1,46 @@
|
||||
name: in_app_review
|
||||
description: Flutter plugin for showing the In-App Review/System Rating pop up on Android, iOS and MacOS. It makes it easy for users to rate your app.
|
||||
version: 2.0.6
|
||||
homepage: https://github.com/britannio/in_app_review/tree/master/in_app_review
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
flutter: ">=2.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
in_app_review_platform_interface: ^2.0.4
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
mockito: ^5.0.0
|
||||
plugin_platform_interface: ^2.0.0
|
||||
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
# This section identifies this Flutter project as a plugin project.
|
||||
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
|
||||
# which should be registered in the plugin registry. This is required for
|
||||
# using method channels.
|
||||
# The Android 'package' specifies package in which the registered class is.
|
||||
# This is required for using method channels on Android.
|
||||
# The 'ffiPlugin' specifies that native code should be built and bundled.
|
||||
# This is required for using `dart:ffi`.
|
||||
# All these are used by the tooling to maintain consistency when
|
||||
# adding or updating assets for this project.
|
||||
plugin:
|
||||
platforms:
|
||||
android:
|
||||
package: dev.britannio.in_app_review
|
||||
pluginClass: InAppReviewPlugin
|
||||
ios:
|
||||
pluginClass: InAppReviewPlugin
|
||||
macos:
|
||||
pluginClass: InAppReviewPlugin
|
12
components/in_app_review_platform_interface/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
.DS_Store
|
||||
.dart_tool/
|
||||
|
||||
.packages
|
||||
.pub/
|
||||
|
||||
build/
|
||||
|
||||
|
||||
pubspec.lock
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
46
components/in_app_review_platform_interface/CHANGELOG.md
Normal file
@ -0,0 +1,46 @@
|
||||
# [2.0.4]
|
||||
|
||||
- Update usage of `pkg:url_launcher` to address deprecations.
|
||||
|
||||
# [2.0.3]
|
||||
|
||||
- `isAvailable()` now returns `false` on web.
|
||||
|
||||
# [2.0.2]
|
||||
|
||||
- Bump the minimum Flutter version to `2.0.0`.
|
||||
|
||||
# [2.0.1]
|
||||
|
||||
- Bump the minimum Dart SDK version from `2.12.0-0` to `2.12.0`.
|
||||
|
||||
# [2.0.0]
|
||||
|
||||
- Migrate to null safety.
|
||||
|
||||
# [1.0.5]
|
||||
|
||||
- Remove dependency on `package_info`.
|
||||
- Handle `openStoreListing()` with native code for Android, iOS and MacOS.
|
||||
|
||||
# [1.0.4]
|
||||
|
||||
- Lower dependency version constraints
|
||||
|
||||
# [1.0.3]
|
||||
|
||||
- Open the App Store directly instead of via the Safari View Controller.
|
||||
- Add automated tests.
|
||||
|
||||
# [1.0.2]
|
||||
|
||||
- Rename `openStoreListing(windowsStoreId: '')` to `openStoreListing(microsoftStoreId: '')`.
|
||||
- Update dependencies.
|
||||
|
||||
# [1.0.1]
|
||||
|
||||
- Remove unnecessary files.
|
||||
|
||||
# [1.0.0]
|
||||
|
||||
- Initial release.
|
21
components/in_app_review_platform_interface/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Britannio Jarrett
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
26
components/in_app_review_platform_interface/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# in_app_review_platform_interface
|
||||
|
||||
A common platform interface for the [`in_app_review`][1] plugin.
|
||||
|
||||
This interface allows platform-specific implementations of the `in_app_review`
|
||||
plugin, as well as the plugin itself, to ensure they are supporting the
|
||||
same interface.
|
||||
|
||||
# Usage
|
||||
|
||||
To implement a new platform-specific implementation of `in_app_review`, extend
|
||||
[`InAppReviewPlatform`][2] with an implementation that performs the
|
||||
platform-specific behavior, and when you register your plugin, set the default
|
||||
`InAppReviewPlatform` by calling
|
||||
`InAppReviewPlatform.instance = MyInAppReview()`.
|
||||
|
||||
# Note on breaking changes
|
||||
|
||||
Strongly prefer non-breaking changes (such as adding a method to the interface)
|
||||
over breaking changes for this package.
|
||||
|
||||
See https://flutter.dev/go/platform-interface-breaking-changes for a discussion
|
||||
on why a less-clean interface is preferable to a breaking change.
|
||||
|
||||
[1]: ../in_app_review
|
||||
[2]: lib/in_app_review_platform_interface.dart
|
@ -0,0 +1,71 @@
|
||||
import 'package:in_app_review_platform_interface/method_channel_in_app_review.dart';
|
||||
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
||||
|
||||
/// The interface that implementations of in_app_review must implement.
|
||||
///
|
||||
/// Platform implementations should extend this class rather than implement it
|
||||
/// as `in_app_review` does not consider newly added methods to be breaking
|
||||
/// changes. Extending this class (using `extends`) ensures that the subclass
|
||||
/// will get the default implementation, while platform implementations that
|
||||
/// `implements` this interface will be broken by newly added
|
||||
/// [InAppReviewPlatform] methods.
|
||||
abstract class InAppReviewPlatform extends PlatformInterface {
|
||||
InAppReviewPlatform() : super(token: _token);
|
||||
|
||||
static InAppReviewPlatform _instance = MethodChannelInAppReview();
|
||||
|
||||
static final Object _token = Object();
|
||||
|
||||
static InAppReviewPlatform get instance => _instance;
|
||||
|
||||
/// Platform-specific plugins should set this with their own platform-specific
|
||||
/// class that extends [InAppReviewPlatform] when they register themselves.
|
||||
static set instance(InAppReviewPlatform instance) {
|
||||
PlatformInterface.verifyToken(instance, _token);
|
||||
_instance = instance;
|
||||
}
|
||||
|
||||
/// Checks if the device is able to show a review dialog.
|
||||
///
|
||||
/// On Android the Google Play Store must be installed and the device must be
|
||||
/// running **Android 5 Lollipop(API 21)** or higher.
|
||||
///
|
||||
/// iOS devices must be running **iOS version 10.3** or higher.
|
||||
///
|
||||
/// MacOS devices must be running **MacOS version 10.14** or higher
|
||||
Future<bool> isAvailable() {
|
||||
throw UnimplementedError('isAvailable() has not been implemented.');
|
||||
}
|
||||
|
||||
/// Attempts to show the review dialog. It's recommended to first check if
|
||||
/// this cannot be done via [isAvailable]. If it is not available then
|
||||
/// you can open the store listing via [openStoreListing].
|
||||
///
|
||||
/// To improve the users experience, iOS and Android enforce limitations
|
||||
/// that might prevent this from working after a few tries. iOS & MacOS users
|
||||
/// can also disable this feature entirely in the App Store settings.
|
||||
///
|
||||
/// More info and guidance:
|
||||
/// https://developer.android.com/guide/playcore/in-app-review#when-to-request
|
||||
/// https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/ratings-and-reviews/
|
||||
/// https://developer.apple.com/design/human-interface-guidelines/macos/system-capabilities/ratings-and-reviews/
|
||||
Future<void> requestReview() {
|
||||
throw UnimplementedError('requestReview() has not been implemented.');
|
||||
}
|
||||
|
||||
/// Opens the Play Store on Android, the App Store with a review
|
||||
/// screen on iOS & MacOS and the Microsoft Store on Windows.
|
||||
///
|
||||
/// [appStoreId] is required for iOS & MacOS.
|
||||
///
|
||||
/// [microsoftStoreId] is required for Windows.
|
||||
Future<void> openStoreListing({
|
||||
/// Required for iOS & MacOS.
|
||||
String? appStoreId,
|
||||
|
||||
/// Required for Windows.
|
||||
String? microsoftStoreId,
|
||||
}) {
|
||||
throw UnimplementedError('openStoreListing() has not been implemented.');
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
|
||||
import 'in_app_review_platform_interface.dart';
|
||||
|
||||
/// An implementation of [InAppReviewPlatform] that uses method channels.
|
||||
class MethodChannelInAppReview extends InAppReviewPlatform {
|
||||
MethodChannel _channel = MethodChannel('dev.britannio.in_app_review');
|
||||
Platform _platform = const LocalPlatform();
|
||||
|
||||
@visibleForTesting
|
||||
set channel(MethodChannel channel) => _channel = channel;
|
||||
|
||||
@visibleForTesting
|
||||
set platform(Platform platform) => _platform = platform;
|
||||
|
||||
@override
|
||||
Future<bool> isAvailable() async {
|
||||
if (kIsWeb) return false;
|
||||
return _channel
|
||||
.invokeMethod<bool>('isAvailable')
|
||||
.then((bool? available) => available ?? false, onError: (_) => false);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> requestReview() => _channel.invokeMethod('requestReview');
|
||||
|
||||
@override
|
||||
Future<void> openStoreListing({
|
||||
String? appStoreId,
|
||||
String? microsoftStoreId,
|
||||
}) async {
|
||||
final bool isiOS = _platform.isIOS;
|
||||
final bool isMacOS = _platform.isMacOS;
|
||||
final bool isAndroid = _platform.isAndroid;
|
||||
final bool isWindows = _platform.isWindows;
|
||||
|
||||
if (isiOS || isMacOS) {
|
||||
await _channel.invokeMethod(
|
||||
'openStoreListing',
|
||||
ArgumentError.checkNotNull(appStoreId, 'appStoreId'),
|
||||
);
|
||||
} else if (isAndroid) {
|
||||
await _channel.invokeMethod('openStoreListing');
|
||||
} else if (isWindows) {
|
||||
ArgumentError.checkNotNull(microsoftStoreId, 'microsoftStoreId');
|
||||
await _launchUrl(
|
||||
'ms-windows-store://review/?ProductId=$microsoftStoreId',
|
||||
);
|
||||
} else {
|
||||
throw UnsupportedError(
|
||||
'Platform(${_platform.operatingSystem}) not supported',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _launchUrl(String url) async {
|
||||
if (!await canLaunchUrlString(url)) return;
|
||||
await launchUrlString(url, mode: LaunchMode.externalNonBrowserApplication);
|
||||
}
|
||||
}
|
24
components/in_app_review_platform_interface/pubspec.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
name: in_app_review_platform_interface
|
||||
description: A common platform interface for the in_app_review plugin.
|
||||
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
|
||||
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
|
||||
version: 2.0.4
|
||||
homepage: https://github.com/britannio/in_app_review/tree/master/in_app_review_platform_interface
|
||||
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
flutter: ">=2.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
url_launcher: ^6.1.0
|
||||
plugin_platform_interface: ^2.0.0
|
||||
platform: ^3.0.0
|
||||
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
@ -76,6 +76,15 @@ final class SharedPrefsCore {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fileprivate func remove(key: String?) -> Bool{
|
||||
if let key = key {
|
||||
let keyStore = NSUbiquitousKeyValueStore()
|
||||
keyStore.removeObject(forKey: key)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public class SwiftSyncedSharedPreferencesPlugin: NSObject, FlutterPlugin {
|
||||
@ -87,6 +96,14 @@ public class SwiftSyncedSharedPreferencesPlugin: NSObject, FlutterPlugin {
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "remove":
|
||||
if let params = call.arguments as? [String: Any] {
|
||||
let key = params[keyKey] as? String
|
||||
|
||||
let res = SharedPrefsCore.shared.remove(key: key)
|
||||
result(res)
|
||||
}
|
||||
|
||||
case "setBool":
|
||||
if let params = call.arguments as? [String: Any] {
|
||||
let val = params[valKey] as? Bool
|
||||
|
@ -15,6 +15,14 @@ class SyncedSharedPreferences {
|
||||
const MethodChannel(channel),
|
||||
);
|
||||
|
||||
Future<bool?> remove({
|
||||
required String key,
|
||||
}) async {
|
||||
return _channel.invokeMethod('remove', <String, dynamic>{
|
||||
'key': key,
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool?> setBool({
|
||||
required String key,
|
||||
required bool val,
|
||||
|
1
fastlane/metadata/android/en-US/changelogs/108.txt
Normal file
@ -0,0 +1 @@
|
||||
- Navigation shortcuts.
|
1
fastlane/metadata/android/en-US/changelogs/121.txt
Normal file
@ -0,0 +1 @@
|
||||
- Ability to mark a story as read once scrolling past.
|
2
fastlane/metadata/android/en-US/changelogs/125.txt
Normal file
@ -0,0 +1,2 @@
|
||||
- Ability to customize text scale factor.
|
||||
- Ability to customize app's accent color.
|
4
fastlane/metadata/android/en-US/changelogs/127.txt
Normal file
@ -0,0 +1,4 @@
|
||||
- Ability to use Material 3.
|
||||
- Ability to search in thread.
|
||||
- Ability to customize text scale factor.
|
||||
- Ability to customize app's accent color.
|
5
fastlane/metadata/android/en-US/changelogs/128.txt
Normal file
@ -0,0 +1,5 @@
|
||||
- Ability to use pagination on home screen.
|
||||
- Ability to use Material 3 (experimental).
|
||||
- Ability to search in thread.
|
||||
- Ability to customize text scale factor.
|
||||
- Ability to customize app's accent color.
|
5
fastlane/metadata/android/en-US/changelogs/129.txt
Normal file
@ -0,0 +1,5 @@
|
||||
- Ability to use manual pagination on home screen.
|
||||
- Ability to use Material 3 (experimental).
|
||||
- Ability to search in thread.
|
||||
- Ability to customize text scale factor.
|
||||
- Ability to customize app's accent color.
|
4
fastlane/metadata/android/en-US/changelogs/131.txt
Normal file
@ -0,0 +1,4 @@
|
||||
- New comment indicator.
|
||||
- Ability to mark stories as read from home page.
|
||||
- Text rendering improvements.
|
||||
- Performance improvements.
|
4
fastlane/metadata/android/en-US/changelogs/132.txt
Normal file
@ -0,0 +1,4 @@
|
||||
- New comment indicator.
|
||||
- Ability to mark stories as read from home page.
|
||||
- Text rendering improvements.
|
||||
- Performance improvements.
|
4
fastlane/metadata/android/en-US/changelogs/134.txt
Normal file
@ -0,0 +1,4 @@
|
||||
- RobotoSlab as default font.
|
||||
- Material 3 design.
|
||||
- Ability to sync favorites from your Hacker News account.
|
||||
- Support for predictive back gesture.
|
3
fastlane/metadata/android/en-US/changelogs/135.txt
Normal file
@ -0,0 +1,3 @@
|
||||
- Return of true dark mode.
|
||||
- Better comment fetching strategy.
|
||||
- Minor UI fixes.
|
Before Width: | Height: | Size: 522 KiB |
Before Width: | Height: | Size: 835 KiB |
Before Width: | Height: | Size: 282 KiB |
Before Width: | Height: | Size: 298 KiB |
Before Width: | Height: | Size: 820 KiB |
Before Width: | Height: | Size: 868 KiB |
Before Width: | Height: | Size: 121 KiB |
Before Width: | Height: | Size: 375 KiB |
Before Width: | Height: | Size: 282 KiB |
Before Width: | Height: | Size: 414 KiB |
Before Width: | Height: | Size: 530 KiB |
Before Width: | Height: | Size: 406 KiB |
After Width: | Height: | Size: 1003 KiB |
After Width: | Height: | Size: 912 KiB |
After Width: | Height: | Size: 252 KiB |
After Width: | Height: | Size: 734 KiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 893 KiB |