diff --git a/games_services/android/build.gradle b/games_services/android/build.gradle index eb07506..3d0cf8c 100644 --- a/games_services/android/build.gradle +++ b/games_services/android/build.gradle @@ -2,7 +2,7 @@ group 'com.abedalkareem.games_services' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.8.10' + ext.kotlin_version = '1.9.0' repositories { google() mavenCentral() @@ -11,7 +11,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:7.0.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.google.gms:google-services:4.3.15' + classpath 'com.google.gms:google-services:4.4.2' } } @@ -26,8 +26,6 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 31 - if (project.android.hasProperty('namespace')) { namespace 'com.abedalkareem.games_services' } @@ -36,7 +34,8 @@ android { main.java.srcDirs += 'src/main/kotlin' } defaultConfig { - minSdkVersion 16 + compileSdk 34 + minSdkVersion 19 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" consumerProguardFiles "proguard-rules.pro" } @@ -48,6 +47,6 @@ android { dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation 'com.google.code.gson:gson:2.10.1' - api "com.google.android.gms:play-services-games-v2:19.0.0" - api 'com.google.android.gms:play-services-auth:20.7.0' + api "com.google.android.gms:play-services-games-v2:20.1.2" + api 'com.google.android.gms:play-services-auth:21.2.0' } diff --git a/games_services/android/gradle/wrapper/gradle-wrapper.jar b/games_services/android/gradle/wrapper/gradle-wrapper.jar index 13372ae..7454180 100644 Binary files a/games_services/android/gradle/wrapper/gradle-wrapper.jar and b/games_services/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/games_services/android/gradle/wrapper/gradle-wrapper.properties b/games_services/android/gradle/wrapper/gradle-wrapper.properties index 0b60b61..ffed3a2 100644 --- a/games_services/android/gradle/wrapper/gradle-wrapper.properties +++ b/games_services/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sat May 13 15:10:21 CEST 2023 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/games_services/android/gradlew b/games_services/android/gradlew index 9d82f78..1b6c787 100755 --- a/games_services/android/gradlew +++ b/games_services/android/gradlew @@ -1,74 +1,129 @@ -#!/usr/bin/env bash +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum -warn ( ) { +warn () { echo "$*" -} +} >&2 -die ( ) { +die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -77,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -85,76 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=$((i+1)) + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/games_services/android/gradlew.bat b/games_services/android/gradlew.bat index aec9973..ac1b06f 100644 --- a/games_services/android/gradlew.bat +++ b/games_services/android/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -8,20 +24,23 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,34 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell diff --git a/games_services/android/src/main/kotlin/com/abedalkareem/games_services/SaveGame.kt b/games_services/android/src/main/kotlin/com/abedalkareem/games_services/SaveGame.kt index 24ca035..905c7c9 100644 --- a/games_services/android/src/main/kotlin/com/abedalkareem/games_services/SaveGame.kt +++ b/games_services/android/src/main/kotlin/com/abedalkareem/games_services/SaveGame.kt @@ -1,5 +1,6 @@ package com.abedalkareem.games_services +import android.util.Log import com.abedalkareem.games_services.models.SavedGame import com.abedalkareem.games_services.util.PluginError import com.abedalkareem.games_services.util.errorCode @@ -13,17 +14,22 @@ import io.flutter.plugin.common.MethodChannel class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { + private val tag = "SaveGame" + private val snapshotsClient: SnapshotsClient get() { return PlayGames.getSnapshotsClient(activityPluginBinding.activity) } fun getSavedGames(result: MethodChannel.Result) { + Log.d(tag, "[GetSavedGames] Start loading all saved games") snapshotsClient.load(true) .addOnSuccessListener { annotatedData -> + val gson = Gson() val data = annotatedData.get() if (data == null) { + Log.d(tag, "[GetSavedGames] Something went wrong data is null") result.error( PluginError.FailedToGetSavedGames.errorCode(), PluginError.FailedToGetSavedGames.errorMessage(), @@ -35,11 +41,13 @@ class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { .toList() .map { SavedGame(it.uniqueName, it.lastModifiedTimestamp, it.deviceName) } + Log.d(tag, "[GetSavedGames] Loaded successfully") val string = gson.toJson(items) ?: "" result.success(string) data.release() } .addOnFailureListener { + Log.d(tag, "[GetSavedGames] Something went wrong ${it.localizedMessage}") result.error( PluginError.FailedToGetSavedGames.errorCode(), it.localizedMessage, @@ -51,6 +59,7 @@ class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { fun saveGame( data: String, desc: String, name: String, result: MethodChannel.Result ) { + Log.d(tag, "[SaveGame] Start saving game") val metadataChange = SnapshotMetadataChange.Builder() .setDescription(desc) .build() @@ -62,15 +71,19 @@ class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { // Set the data payload for the snapshot snapshot.snapshotContents.writeBytes(data.toByteArray()) + Log.d(tag, "[SaveGame] Start commit") // Commit the operation snapshotsClient.commitAndClose(snapshot, metadataChange) .addOnSuccessListener { + Log.d(tag, "[SaveGame] Loaded successfully") result.success(null) } .addOnFailureListener { + Log.d(tag, "[SaveGame] Something went wrong while commit ${it.localizedMessage}") result.error(PluginError.FailedToSaveGame.errorCode(), it.localizedMessage, null) } } else { + Log.d(tag, "[SaveGame] Something went wrong snapshot is null $annotatedData") result.error( PluginError.FailedToSaveGame.errorCode(), PluginError.FailedToSaveGame.errorMessage(), @@ -79,6 +92,7 @@ class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { } } .addOnFailureListener { + Log.d(tag, "[SaveGame] Failed with error ${it.localizedMessage}") result.error( PluginError.FailedToSaveGame.errorCode(), it.localizedMessage, @@ -88,9 +102,11 @@ class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { } fun deleteGame(name: String, result: MethodChannel.Result) { + Log.d(tag, "[DeleteGame] Start delete game") // Open the saved game using its name. snapshotsClient.open(name, false, SnapshotsClient.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED) .addOnFailureListener { + Log.d(tag, "[DeleteGame] Open failed with error ${it.localizedMessage}") result.error( PluginError.FailedToDeleteSavedGame.errorCode(), it.localizedMessage ?: "", @@ -99,7 +115,9 @@ class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { } .continueWith { snapshotOrConflict -> val snapshot = snapshotOrConflict.result.data + Log.d(tag, "[DeleteGame] Got result") if (snapshot?.metadata == null) { + Log.d(tag, "[DeleteGame] Meta data is null $snapshot") result.error( PluginError.FailedToDeleteSavedGame.errorCode(), PluginError.FailedToDeleteSavedGame.errorMessage(), @@ -107,11 +125,14 @@ class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { ) return@continueWith } + Log.d(tag, "[DeleteGame] Start deleting snapshot") snapshotsClient.delete(snapshot.metadata) .addOnSuccessListener { + Log.d(tag, "[DeleteGame] Deleted successfully") result.success(it) } .addOnFailureListener { + Log.d(tag, "[DeleteGame] Something went wrong deleting snapshot ${it.localizedMessage}") result.error( PluginError.FailedToDeleteSavedGame.errorCode(), it.localizedMessage ?: "", @@ -122,9 +143,11 @@ class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { } fun loadGame(name: String, result: MethodChannel.Result) { + Log.d(tag, "[LoadGame] Load game started") // Open the saved game using its name. snapshotsClient.open(name, false, SnapshotsClient.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED) .addOnFailureListener { + Log.d(tag, "[LoadGame] Failed to open a game with name ${name}, error ${it.localizedMessage}") result.error( PluginError.FailedToLoadGame.errorCode(), it.localizedMessage ?: "", @@ -133,14 +156,16 @@ class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { } .continueWith { val snapshot = it.result.data - + Log.d(tag, "[LoadGame] Got the result") // Opening the snapshot was a success and any conflicts have been resolved. try { // Extract the raw data from the snapshot. val value = snapshot?.snapshotContents?.readFully() if (value != null) { + Log.d(tag, "[LoadGame] Loaded game successfully") result.success(String(value)) } else { + Log.d(tag, "[LoadGame] Failed to read fully $snapshot") result.error( PluginError.FailedToLoadGame.errorCode(), PluginError.FailedToLoadGame.errorMessage(), @@ -148,6 +173,7 @@ class SaveGame(private var activityPluginBinding: ActivityPluginBinding) { ) } } catch (exception: Exception) { + Log.d(tag, "[LoadGame] Something went wrong ${exception.localizedMessage}") result.error( PluginError.FailedToLoadGame.errorCode(), exception.localizedMessage ?: "", null diff --git a/games_services/darwin/Classes/SaveGame.swift b/games_services/darwin/Classes/SaveGame.swift index 0ec76a8..1f45466 100644 --- a/games_services/darwin/Classes/SaveGame.swift +++ b/games_services/darwin/Classes/SaveGame.swift @@ -8,21 +8,31 @@ import FlutterMacOS class SaveGame: BaseGamesServices { func saveGame(name: String, data: String, result: @escaping FlutterResult) { + log("[SaveGame] Start saving game") guard let data = data.data(using: .utf8) else { + log("[SaveGame] failed to get data from a string \(data)") result(PluginError.failedToSaveGame.flutterError) - return } + return + } + + log("[SaveGame] Start saving the game to current player") currentPlayer.saveGameData(data, withName: name) { savedGame, error in guard error == nil else { + log("[SaveGame] Something went wrong \(error?.localizedDescription ?? "")") result(error?.flutterError(code: .failedToSaveGame)) return } + + log("[SaveGame] Saved successfully") result(nil) } } func getSavedGames(result: @escaping FlutterResult) { currentPlayer.fetchSavedGames(completionHandler: { savedGames, error in + log("[GetSavedGames] Start loading all saved games") guard error == nil else { + log("[GetSavedGames] Something went wrong \(error?.localizedDescription ?? "")") result(error?.flutterError(code: .failedToGetSavedGames)) return } @@ -31,31 +41,41 @@ class SaveGame: BaseGamesServices { modificationDate: UInt64($0.modificationDate?.timeIntervalSince1970 ?? 0), deviceName: $0.deviceName ?? "") }) ?? [] if let data = try? JSONEncoder().encode(items) { + log("[GetSavedGames] Loaded successfully") let string = String(data: data, encoding: String.Encoding.utf8) result(string) } else { + log("[GetSavedGames] Something wrong with the data \(items)") result(PluginError.failedToGetSavedGames.flutterError()) } }) } func loadGame(name: String, result: @escaping FlutterResult) { + log("[LoadGame] Start loading the saved games") currentPlayer.fetchSavedGames(completionHandler: { savedGames, error in + log("[LoadGame] Got saved games result") guard error == nil else { + log("[LoadGame] something went wrong \(error?.localizedDescription ?? "")") result(error?.flutterError(code: .failedToLoadGame)) return } guard let item = savedGames? .first(where: { $0.name == name }) else { + log("[LoadGame] Saved game not found") result(PluginError.failedToLoadGame.flutterError()) return } + + log("[LoadGame] Start loading the saved game data") item.loadData { data, error in guard let data = data, error == nil else { + log("[LoadGame] Something went wrong loading the saved game data \(error?.localizedDescription ?? "")") result(error?.flutterError(code: .failedToLoadGame)) return } + log("[LoadGame] Got the saved game data") let string = String(data: data, encoding: String.Encoding.utf8) result(string) } @@ -63,11 +83,14 @@ class SaveGame: BaseGamesServices { } func deleteGame(name: String, result: @escaping FlutterResult) { + log("[DeleteGame] Start deleting game") currentPlayer.deleteSavedGames(withName: name) { error in guard error == nil else { + log("[DeleteGame] Something went wrong \(error?.localizedDescription ?? "")") result(error?.flutterError(code: .failedToDeleteSavedGame)) return } + log("[DeleteGame] Deleted successfully") result(nil) } } diff --git a/games_services/darwin/Classes/Util/log.swift b/games_services/darwin/Classes/Util/log.swift new file mode 100644 index 0000000..c217b9b --- /dev/null +++ b/games_services/darwin/Classes/Util/log.swift @@ -0,0 +1,14 @@ +// +// log.swift +// games_services +// +// Created by abedalkareem omreyh on 08/08/2024. +// + +import Foundation + +func log(_ message: String) { + #if DEBUG + print("[games_services] \(message)") + #endif +} diff --git a/games_services/example/android/app/build.gradle b/games_services/example/android/app/build.gradle index 61ccc7d..01fb2d6 100644 --- a/games_services/example/android/app/build.gradle +++ b/games_services/example/android/app/build.gradle @@ -26,7 +26,9 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 32 + defaultConfig { + compileSdk 34 + } namespace 'com.abedalkareem.game_services_example' @@ -37,8 +39,8 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.abedalkareem.games_services_example" - minSdkVersion 16 - targetSdkVersion 32 + minSdkVersion 23 + targetSdkVersion 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/games_services/example/android/app/src/main/AndroidManifest.xml b/games_services/example/android/app/src/main/AndroidManifest.xml index ef90f55..edf814f 100644 --- a/games_services/example/android/app/src/main/AndroidManifest.xml +++ b/games_services/example/android/app/src/main/AndroidManifest.xml @@ -9,7 +9,7 @@ android:label="games_services_example" android:icon="@mipmap/ic_launcher"> 'BSD' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '11.0' + s.ios.deployment_target = '12.0' # Framework linking is handled by Flutter tooling, not CocoaPods. # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. s.vendored_frameworks = 'path/to/nothing' diff --git a/games_services/example/ios/Podfile.lock b/games_services/example/ios/Podfile.lock index ea8e8f4..5dd6bc2 100644 --- a/games_services/example/ios/Podfile.lock +++ b/games_services/example/ios/Podfile.lock @@ -15,9 +15,9 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/games_services/darwin" SPEC CHECKSUMS: - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 games_services: 9d174ae29e8de7aa3f511bc00fedd7006594063f PODFILE CHECKSUM: 1d687fa337e018c706d6fe47544322b4937fa8f6 -COCOAPODS: 1.13.0 +COCOAPODS: 1.14.3 diff --git a/games_services/lib/src/leaderboards.dart b/games_services/lib/src/leaderboards.dart index 45edc99..0329540 100644 --- a/games_services/lib/src/leaderboards.dart +++ b/games_services/lib/src/leaderboards.dart @@ -10,8 +10,9 @@ abstract class Leaderboards { static Future showLeaderboards( {iOSLeaderboardID = "", androidLeaderboardID = ""}) async { return await GamesServicesPlatform.instance.showLeaderboards( - iOSLeaderboardID: iOSLeaderboardID, - androidLeaderboardID: androidLeaderboardID); + iOSLeaderboardID: iOSLeaderboardID, + androidLeaderboardID: androidLeaderboardID, + ); } /// Get leaderboard scores as a list. Use this to build a custom UI. @@ -23,11 +24,12 @@ abstract class Leaderboards { required TimeScope timeScope, required int maxResults}) async { final response = await GamesServicesPlatform.instance.loadLeaderboardScores( - androidLeaderboardID: androidLeaderboardID, - iOSLeaderboardID: iOSLeaderboardID, - scope: scope, - timeScope: timeScope, - maxResults: maxResults); + androidLeaderboardID: androidLeaderboardID, + iOSLeaderboardID: iOSLeaderboardID, + scope: scope, + timeScope: timeScope, + maxResults: maxResults, + ); if (response != null) { Iterable items = json.decode(response) as List; return List.from( @@ -42,11 +44,13 @@ abstract class Leaderboards { androidLeaderboardID = "", required PlayerScope scope, required TimeScope timeScope}) async { - final String? response = await GamesServicesPlatform.instance.getPlayerScoreObject( - androidLeaderboardID: androidLeaderboardID, - iOSLeaderboardID: iOSLeaderboardID, - scope: scope, - timeScope: timeScope); + final String? response = + await GamesServicesPlatform.instance.getPlayerScoreObject( + androidLeaderboardID: androidLeaderboardID, + iOSLeaderboardID: iOSLeaderboardID, + scope: scope, + timeScope: timeScope, + ); return LeaderboardScoreData.fromJson(json.decode(response ?? "")); } diff --git a/games_services/pubspec.lock b/games_services/pubspec.lock index 8760971..da2cf83 100644 --- a/games_services/pubspec.lock +++ b/games_services/pubspec.lock @@ -34,10 +34,10 @@ packages: dependency: "direct main" description: name: games_services_platform_interface - sha256: be5a69656d0b76dd03a7d3d3db5c3eec5d85c51d44c862d83761576fa6cb4f13 + sha256: "3742c23afdd5ebd5498612b13dc251853fccf935f6f56ef6bf13c6e8c0c89a8d" url: "https://pub.dev" source: hosted - version: "4.0.1" + version: "4.0.2" lints: dependency: transitive description: @@ -50,26 +50,26 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.15.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface - sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "2.1.6" + version: "2.1.8" sky_engine: dependency: transitive description: flutter @@ -83,14 +83,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: - dependency: transitive - description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 - url: "https://pub.dev" - source: hosted - version: "0.3.0" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" + dart: ">=3.3.0-0 <4.0.0" flutter: ">=1.22.0" diff --git a/games_services/pubspec.yaml b/games_services/pubspec.yaml index 8b30256..59e2631 100644 --- a/games_services/pubspec.yaml +++ b/games_services/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: flutter: sdk: flutter - games_services_platform_interface: ^4.0.1 + games_services_platform_interface: ^4.0.2 #path: ../games_services_platform_interface/ #uncomment in time of development. # git: diff --git a/games_services_platform_interface/CHANGELOG.md b/games_services_platform_interface/CHANGELOG.md index ea0e8dd..fd9ab34 100644 --- a/games_services_platform_interface/CHANGELOG.md +++ b/games_services_platform_interface/CHANGELOG.md @@ -1,3 +1,11 @@ +## 4.0.2 +- Add getPlayerScoreObject to retrieve rank and other score data for leaderboard and time span. by @egonbeermat +- Add optional String token to methods that submit and retrieve score. by @egonbeermat +- Add optional Bool showsCompletionBanner as parameter for iOS GKAchievement.report, replacing hard-coded true. Defaults to true. by @egonbeermat +- Add resetAchievements method for iOS only, Android doesn't support this through Play Games. by @egonbeermat +- Export for player_data.dart. by @@theLee3 +- Add logs to save game. + ## 4.0.1 - Add ability to get Play Games auth code for use with backends by @theLee3 - Add PlayerData class to return more score holder details by @theLee3 diff --git a/games_services_platform_interface/pubspec.yaml b/games_services_platform_interface/pubspec.yaml index 9981f6a..67612cb 100644 --- a/games_services_platform_interface/pubspec.yaml +++ b/games_services_platform_interface/pubspec.yaml @@ -1,7 +1,7 @@ name: games_services_platform_interface description: A common platform interface for the games_services plugin. homepage: https://github.com/Abedalkareem/games_services -version: 4.0.1 +version: 4.0.2 environment: sdk: '>=2.12.0 <4.0.0'