Skip to content

Commit 8fdb6e1

Browse files
authored
Merge pull request #19139 from hrydgard/android-upgrade-sdk
Android: Upgrade SDK and target versions, implement shortcut icons
2 parents 9ab775f + ec5eff6 commit 8fdb6e1

3 files changed

Lines changed: 104 additions & 16 deletions

File tree

android/build.gradle

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ androidGitVersion {
1111
}
1212

1313
dependencies {
14+
// 1.2.0 is the newest version we can use that won't complain about minSdk version.
1415
def appcompat_version = "1.2.0"
1516

1617
implementation "androidx.appcompat:appcompat:$appcompat_version"
@@ -21,7 +22,7 @@ dependencies {
2122
}
2223

2324
android {
24-
flavorDimensions "variant"
25+
flavorDimensions += "variant"
2526
namespace 'org.ppsspp.ppsspp'
2627
signingConfigs {
2728
debug {
@@ -45,10 +46,20 @@ android {
4546
}
4647
}
4748

48-
compileSdk 33
49+
compileSdk 34
4950
ndkVersion "21.4.7075529"
5051

5152
defaultConfig {
53+
/*
54+
configurations.all {
55+
resolutionStrategy {
56+
// Newer versions are not compatible with our minsdk. Should find a way to exclude it entirely
57+
// since we have no use for this transitive dependency.
58+
force 'androidx.emoji2:emoji2-views-helper:1.0.0'
59+
}
60+
}
61+
*/
62+
5263
applicationId 'org.ppsspp.ppsspp'
5364
if (androidGitVersion.name() != "unknown" && androidGitVersion.code() >= 14000000) {
5465
// Start using automatic Android version numbers from version 1.4.
@@ -63,7 +74,7 @@ android {
6374
new File("versioncode.txt").write(androidGitVersion.code().toString())
6475

6576
minSdk 9
66-
targetSdk 33
77+
targetSdk 34
6778
if (project.hasProperty("ANDROID_VERSION_CODE") && project.hasProperty("ANDROID_VERSION_NAME")) {
6879
versionCode ANDROID_VERSION_CODE
6980
versionName ANDROID_VERSION_NAME

android/jni/app-android.cpp

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,8 @@ bool System_GetPropertyBool(SystemProperty prop) {
547547
return deviceType != DEVICE_TYPE_VR;
548548
case SYSPROP_HAS_ACCELEROMETER:
549549
return deviceType == DEVICE_TYPE_MOBILE;
550+
case SYSPROP_CAN_CREATE_SHORTCUT:
551+
return false; // We can't create shortcuts directly from game code, but we can from the Android UI.
550552
#ifndef HTTPS_NOT_AVAILABLE
551553
case SYSPROP_SUPPORTS_HTTPS:
552554
return !g_Config.bDisableHTTPS;
@@ -1662,7 +1664,7 @@ static void VulkanEmuThread(ANativeWindow *wnd) {
16621664

16631665
// NOTE: This is defunct and not working, due to how the Android storage functions currently require
16641666
// a PpssppActivity specifically and we don't have one here.
1665-
extern "C" jstring Java_org_ppsspp_ppsspp_ShortcutActivity_queryGameName(JNIEnv *env, jclass, jstring jpath) {
1667+
extern "C" jstring Java_org_ppsspp_ppsspp_ShortcutActivity_queryGameName(JNIEnv * env, jclass, jstring jpath) {
16661668
bool teardownThreadManager = false;
16671669
if (!g_threadManager.IsInitialized()) {
16681670
INFO_LOG(SYSTEM, "No thread manager - initializing one");
@@ -1710,3 +1712,58 @@ extern "C" jstring Java_org_ppsspp_ppsspp_ShortcutActivity_queryGameName(JNIEnv
17101712

17111713
return env->NewStringUTF(result.c_str());
17121714
}
1715+
1716+
1717+
extern "C"
1718+
JNIEXPORT jbyteArray JNICALL
1719+
Java_org_ppsspp_ppsspp_ShortcutActivity_queryGameIcon(JNIEnv * env, jclass clazz, jstring jpath) {
1720+
bool teardownThreadManager = false;
1721+
if (!g_threadManager.IsInitialized()) {
1722+
INFO_LOG(SYSTEM, "No thread manager - initializing one");
1723+
// Need a thread manager.
1724+
teardownThreadManager = true;
1725+
g_threadManager.Init(1, 1);
1726+
}
1727+
// TODO: implement requestIcon()
1728+
1729+
Path path = Path(GetJavaString(env, jpath));
1730+
1731+
INFO_LOG(SYSTEM, "queryGameIcon(%s)", path.c_str());
1732+
1733+
jbyteArray result = nullptr;
1734+
1735+
GameInfoCache *cache = new GameInfoCache();
1736+
std::shared_ptr<GameInfo> info = cache->GetInfo(nullptr, path, GameInfoFlags::ICON);
1737+
// Wait until it's done: this is synchronous, unfortunately.
1738+
if (info) {
1739+
INFO_LOG(SYSTEM, "GetInfo successful, waiting");
1740+
int attempts = 1000;
1741+
while (!info->Ready(GameInfoFlags::ICON)) {
1742+
sleep_ms(1);
1743+
attempts--;
1744+
if (!attempts) {
1745+
break;
1746+
}
1747+
}
1748+
INFO_LOG(SYSTEM, "Done waiting");
1749+
if (info->Ready(GameInfoFlags::ICON)) {
1750+
if (!info->icon.data.empty()) {
1751+
INFO_LOG(SYSTEM, "requestIcon: Got icon");
1752+
result = env->NewByteArray(info->icon.data.size());
1753+
env->SetByteArrayRegion(result, 0, info->icon.data.size(), (const jbyte *)info->icon.data.data());
1754+
}
1755+
} else {
1756+
INFO_LOG(SYSTEM, "requestIcon: Filetype unknown");
1757+
}
1758+
} else {
1759+
INFO_LOG(SYSTEM, "No info from cache");
1760+
}
1761+
1762+
delete cache;
1763+
1764+
if (teardownThreadManager) {
1765+
g_threadManager.Teardown();
1766+
}
1767+
1768+
return result;
1769+
}

android/src/org/ppsspp/ppsspp/ShortcutActivity.java

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import android.app.AlertDialog;
55
import android.content.Intent;
66
import android.content.Intent.ShortcutIconResource;
7+
import android.graphics.Bitmap;
8+
import android.graphics.BitmapFactory;
79
import android.net.Uri;
810
import android.os.Build;
911
import android.os.Bundle;
@@ -75,6 +77,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
7577
}
7678

7779
public static native String queryGameName(String path);
80+
public static native byte[] queryGameIcon(String path);
7881

7982
// Create shortcut as response for ACTION_CREATE_SHORTCUT intent.
8083
private void respondToShortcutRequest(Uri uri) {
@@ -99,13 +102,13 @@ private void respondToShortcutRequest(Uri uri) {
99102
if (path.startsWith("content://")) {
100103
String [] segments = path.split("/");
101104
try {
102-
pathStr = java.net.URLDecoder.decode(segments[segments.length - 1], StandardCharsets.UTF_8.name());
105+
pathStr = java.net.URLDecoder.decode(segments[segments.length - 1], "UTF-8");
103106
} catch (Exception e) {
104107
Log.i(TAG, "Exception getting name: " + e);
105108
}
106109
} else if (path.startsWith("file:///")) {
107110
try {
108-
pathStr = java.net.URLDecoder.decode(path.substring(7), StandardCharsets.UTF_8.name());
111+
pathStr = java.net.URLDecoder.decode(path.substring(7), "UTF-8");
109112
} catch (Exception e) {
110113
Log.i(TAG, "Exception getting name: " + e);
111114
}
@@ -116,16 +119,19 @@ private void respondToShortcutRequest(Uri uri) {
116119
String[] pathSegments = pathStr.split("/");
117120
name = pathSegments[pathSegments.length - 1];
118121

119-
/*
120-
// No longer working for various reasons.
121-
122122
PpssppActivity.CheckABIAndLoadLibrary();
123-
String name = queryGameName(path);
124-
if (name.equals("")) {
123+
String gameName = queryGameName(path);
124+
byte [] iconData = null;
125+
if (gameName.equals("")) {
125126
Log.i(TAG, "Failed to retrieve game name - ignoring.");
126-
showBadGameMessage();
127-
return;
128-
}*/
127+
// This probably happened because PPSSPP isn't running so the GameInfoCache isn't working.
128+
// Let's just continue with our fallback name until we can fix that.
129+
// showBadGameMessage();
130+
// return;
131+
} else {
132+
name = gameName;
133+
iconData = queryGameIcon(path);
134+
}
129135

130136
Log.i(TAG, "Game name: " + name + " : Creating shortcut to " + uri.toString());
131137

@@ -134,9 +140,23 @@ private void respondToShortcutRequest(Uri uri) {
134140
Intent responseIntent = new Intent();
135141
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
136142
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
137-
ShortcutIconResource iconResource = ShortcutIconResource.fromContext(this, R.drawable.ic_launcher);
138-
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
139143

144+
boolean setIcon = false;
145+
if (iconData != null) {
146+
// Try to create a PNG from the iconData.
147+
Bitmap bmp = BitmapFactory.decodeByteArray(iconData, 0, iconData.length);
148+
if (bmp != null) {
149+
// Scale it to a square.
150+
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp, 144, 144, true);
151+
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, scaledBitmap);
152+
}
153+
setIcon = true;
154+
}
155+
if (!setIcon) {
156+
// Fall back to the PPSSPP icon.
157+
ShortcutIconResource iconResource = ShortcutIconResource.fromContext(this, R.drawable.ic_launcher);
158+
responseIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource);
159+
}
140160
setResult(RESULT_OK, responseIntent);
141161

142162
// Must call finish for result to be returned immediately

0 commit comments

Comments
 (0)