If GameFile.getCustomCoverPath returns a mangled URI, we need to
unmangle it before passing it to Picasso, since Picasso has no
concept of Dolphin's mangled URIs.
In the past, directory initialization could fail for two reasons:
The user was rejecting the storage permission, or external storage
wasn't mounted. With the introduction of scoped storage, the first of
these two couldn't happen anymore; if the user rejects the storage
permission, we just use the app-specific directory instead of the
dolphin-emu directory.
By making it so Dolphin force quits if external storage isn't mounted,
we can get rid of our code for handling retrying directory initialization
after it fails. I think this slight hit to UX is worth it considering
that basically nobody has an Android device with detachable primary
external storage anymore. And the UX hit is very small; the user just has
to manually open the app again after remounting external storage. The
toast about external storage not being mounted will still be displayed.
The recent merge of the splash screen PR may have made it so that the
code for handling directory initialization failing doesn't work anymore.
To be completely honest, I'm not sure how to even test this in 2022.
For a few years now, I've been thinking it would be nice to make Dolphin
support reading Wii games in the format they come in when you download
them from the Wii U eShop. The Wii U eShop has some good deals on Wii
games (Metroid Prime Trilogy especially is rather expensive if you try
to buy it physically!), and it's the only place right now where you can
buy Wii games digitally.
Of course, Nintendo being Nintendo, next year they're going to shut down
this only place where you can buy Wii games digitally. I kind of wish I
had implemented this feature earlier so that people would've had ample
time to buy the games they want, but... better late than never, right?
I used MIT-licensed code from the NOD library as a reference when
implementing this. None of the code has been directly copied, but
you may notice that the names of the struct members are very similar.
c1635245b8/lib/DiscIONFS.cpp
Without this, if the user finishes selecting a directory before
directory initialization finishes, MainPresenter.onDirectorySelected
will segfault when trying to read the MAIN_RECURSIVE_ISO_PATHS setting.
An alternative would be to use AfterDirectoryInitializationRunner
after the user selects the directory instead of before, but it might
be confusing for the user to deal with the usage statistics prompt
when they were expecting to add a folder.
All of our BBA options are technically built in, so it made the BBA
Built In option kind of confusing as to what it did. So rename it to
BBA HLE to make it more clear what it is doing and why it doesn't need a
TAP.
Android Studio 2022.2 "Chipmunk" changes the code formatting rules a
little. Let's apply the new formatting in this PR so that the lint bot
doesn't take it out on innocent PRs.
Without this, debug builds of Dolphin fail to launch. The OS tries
to locate org.dolphinemu.dolphinemu.debug.DolphinApplication
but fails to find it because its actual name is
org.dolphinemu.dolphinemu.DolphinApplication.
Partially reverts 6b74907f9d.
Create vector Wii and Gamecube icons and re-import default android icons as vector graphics. Scales better on a greater range of devices and takes up less space.
themes.xml now contains a collection of colors, attributes, and styles. No visuals have changed, but this will allow for a more flexible theming system in the future for custom day/night/etc themes. This also removes a bunch of redundant code that can now be written as global styles and inherited themes.
+Remove background on card
+Increase max # of lines for game title
+Root layout is now a linear layout with the card view rounding the corners on the box art
The currentValue variable doesn't use InputOverlay.OVERLAY_
constants, it uses NativeLibrary.ButtonType constants.
Sigh, why do enums have to be so bad on Android that Google
recommends against using them :(
Anyway, simply not doing anything is a reasonable option here.
What happens then is that if the currently selected button is
invalid for the current controller, none of the available options
in the dialog will be pre-selected.
Some ROMs don't have fullscreen feature, for example Pixel Experience, so have a option for that is better. Also you don't need put the app on fullscreen anymore with that.
Use large card view rounded corner guidelines
Fix action bar theming
Needed to import android back button clip art to fix material 3 theming issue. The DolphinSettingsBase style used to inherit from the Theme.MaterialComponents.DayNight.DarkActionBar theme which would provide the light text and icons but this is no longer available with Material 3.
Fit box art more snugly in CardView
Change card height to match cover art
Add padding to top of games list recyclerview
According to the documentation, getActionIndex should only be
used with ACTION_POINTER_DOWN and ACTION_POINTER_UP. We've had a
few crashes reported in the Play Console regarding invalid pointer
indices for getY, and I'm hoping this will help with that.
This saves the GUI from having to manually call SDIO_EventNotify.
With that out of the way, we can let users change the
"Insert SD Card" setting on Android while a game is running.
Apparently there are phones where accessing Dolphin's app-specific
directory isn't just annoyingly hard but actually impossible.
To give users of those phones at least some kind of way to manage
their data (even if it's a lot less convenient than if we were
allowed to let the user open the app-specific directory in a
file manager), I'm adding a way to export the directory to a
zip file and then import it back.
* Disabled: disables the overlay pointer
* Follow: default behaviour, IR pointer follows touch position
* Drag: IR pointer moves relative to the initial touch event position
It's a complete coincidence that both SIDEVICE_GC_CONTROLLER (6)
and SIDEVICE_WIIU_ADAPTER (12) are divisible by 6. Dividing
by 6 because of that doesn't make sense, especially not if we
want to add support for more kinds of SI devices on Android later.
Turns out that most phones ship with a special Google version of
DocumentsUI instead of just using the AOSP version, despite the two
being pretty similar as far as I can tell. This change makes us
check for both package names instead of just the AOSP package name.
I haven't fully confirmed why the previous commit broke this,
but I imagine it's due to AfterDirectoryInitializationRunner
executing in a different order than before, resulting in
startRescan running before startLoad.
This decreases our APK size by a few megabytes. Most of the reduction
is from Java libraries that we only use small parts of. Code shrinking
gets rid of all the unused code from these libraries from the APK.
Because I highly value the ability to get stack traces that make
sense, I have specifically disabled obfuscation (automatic renaming
of symbols to short incomprehensible names).
I've only enabled code shrinking for release builds, purely because
I feel like the extra build time (30 seconds on my machine)
would be annoying when you want to make debug builds rapidly.
This commit changes the default value of Fast Texture Sampling to true, and also moves the setting that controls it to the experimental section of the advanced tab. This is its own commit so that it can be easily reverted when we want to default to Manual Texture Sampling.
Co-authored-by: JosJuice <josjuice@gmail.com>
Gets rid some uses of the deprecated LocalBroadcastManager.
One note about the changes in GameFileCacheManager itself:
The change from compareAndSet to getValue followed by
setValue is actually safe, because startLoad and startRescan
only run from the main thread, and only the main thread ever
sets the flags to true. So it's impossible for any other thread
to change the flag in between the getValue and the setValue.
The past few Android releases have been adding restrictions
to what services are allowed to do, for the sake of stopping
services from using up too much battery in the background.
The IntentService class, which GameFileCacheService uses,
was even deprecated in Android 11 in light of this.
Typically, the reason why you would want use a service instead of
using a simple thread or some other concurrency mechanism from the
Java standard library is if you want to be able to run code in the
background while the user isn't using your app. This isn't actually
something we care about for GameFileCacheService -- if Android wants
to kill Dolphin there's no reason to keep GameFileCacheService
running -- so let's make it not be a service.
I'm changing this mainly for the sake of future proofing, but there
is one immediate (minor) benefit: Previously, if you tried to launch
Dolphin from Android Studio while your phone was locked, the whole
app would fail to launch because launching GameFileCacheService
wasn't allowed because Dolphin wasn't considered a foreground app.
Unlike with Android 11, there should be no downsides to doing
this, so we might as well get this out of the way early.
The main part of the work was already done in 5a1a642.
This should make C++20 and std::filesystem work. (Not that
we really can use std::filesystem much on Android since
it doesn't work with scoped storage...)
This makes Android ask the user whether they want to delete user
data when uninstalling the app instead of always deleting user data,
which is pretty great now that we're forced to use scoped storage.
It only works on Android 10 and up, though.
When I made 9c8bb24, I assumed it was completely impossible for a
non-preloaded app to access the entirety of the Android/data/ folder
on Android 11. This turned out to be false. While you can't access
the directory without using SAF (even if you have the Manage All
Files permission), and the user can't navigate to the folder using
the SAF folder picker, what you can do is pass the Android/data/
folder as an EXTRA_INITIAL_URI to the SAF folder picker. If the
user then presses "use this folder" without navigating out of the
folder, the app will be able to access the folder using SAF.
So what does that mean for Dolphin? It means scoped storage is a
little less bad than I feared, and I have a string to adjust.
Yes, that's right! It's time to add even more NKit warnings,
because users still don't understand what NKit is or how it works!
More specifically, some users seem to be under the impression that
converting an NKit file to for instance RVZ using Dolphin's convert
feature will result in a normal RVZ file, when it in fact results in
an NKit RVZ file (since NKit is not a container format in the sense
that GCZ/WIA/RVZ/WBFS/CISO is, but rather a kind of trimmed ISO).
I can hardly blame users for not knowing this, because it's not
intuitive unless you know the technical details of how NKit works.
The purpose of this class was to keep track of state which the
emulation core was already keeping track of. This is rather risky -
if we update the state of one of the two without updating the other,
the two become out of sync, leading to some rather confusing problems.
This duplicated state was removed from EmulationState in the
previous commits, so now there isn't much left in the class.
Might as well move its members directly into EmulationFragment.
Apparently some phones (at least some from Samsung) don't expose the
system file manager in the system settings despite it being the
only on-device file manager that can open app-specific directories...
This enables scoped storage for new Dolphin installs on Android 11
and up (along with a few other changes in behavior which unlike
scoped storage are uncontroversial). Existing installs are unaffected.
We have to do this in order to be able to release updates on
Google Play from November 2021 and on.
The following settings are currently not SAF compatible,
and might never be due to the performance impact:
Dump Path
Load Path
Resource Pack Path
Wii NAND Root
This commit makes us show a message to the user if they try to
change one of these settings while scoped storage is active.
I don't want to entirely remove the settings from being listed
in the settings activity, because it's important that the user
is able to reset them if they were set to something custom in
a previous version of Dolphin.
This lets Dolphin function without the user granting access to
external storage. We need this for scoped storage compatibility.
When scoped storage is not active, we still ask for permission to
access external storage the first time the app is started so that
we can use the existing dolphin-emu folder if there is one. But
if it doesn't exist, or the user denies the permission, or scoped
storage is active, the app-specific directory will be used instead.
Special shoutout to Android for not having RTL compatible
variants of nextFocusRight and nextFocusLeft.
Ideally we would have some way to block the user from using
the d-pad to switch between the two panes when in portrait mode,
or make the list pane act as if it's to the left of the details
pane rather than the right when the details pane is open, but I
don't know of a good way to do this. SlidingPaneLayout doesn't
really seem to have been implemented with d-pad navigation in mind.
Thankfully, landscape is the most important use case for gamepads.
The way I'm implementing events using LiveData feels rather
unorthodox, but I'm not aware of anything in the Android framework
that would let me do it in a better way... One option I did
consider was wrapping the cheat lists in LiveData and observing
those, but then CheatsAdapter wouldn't know which cheat had
changed, only that there was some kind of change to the list,
necessitating the use of the not recommended notifyDataSetChanged.
This path isn't really any faster in the normal case,
but it does let us skip waiting for the lock to be available,
which makes a huge difference if the lock is already taken.
It seems like we spend a lot of the game list scanning time in
updateAdditionalMetadata, which I suppose makes sense considering
how many different files that function attempts to open.
With the addition of just one little atomic operation, we can make
it safe to call updateAdditionalMetadata without holding a lock.
FindAllGamePaths may take a little while, and holding the
gameFileCache lock isn't actually necessary until it's time to
put the results returned by FindAllGamePaths into gameFileCache.
The downside of this change is that we have to do an extra
round of JNI in between FindAllGamePaths and Update,
but I don't think that's much of a problem.
SPDX standardizes how source code conveys its copyright and licensing
information. See https://spdx.github.io/spdx-spec/1-rationale/ . SPDX
tags are adopted in many large projects, including things like the Linux
kernel.
This partially reverts commit cbc4989095
due to a crash: https://bugs.dolphin-emu.org/issues/12561
I can't debug what the cause of the crash is due to not having an
Android TV device. Let's just revert this for now to fix the crash.
The same kind of change as the changes made in the previous
commit, but this change is more involved, in particular because
of how SyncProgramsJobService was using display names as keys.
Now that DOL and ELF files are assigned game IDs, all games have
game IDs. (Unless you intentionally craft an ISO file that has
the first bytes set to null, but if you do that I think you can
live with Dolphin creating a file in GameSettings called ".ini")
Back when I wrote this code, I believe I set it to use a custom path
so that the cache would end up in a directory which Android considers
to be a cache directory. But nowadays the directory which Dolphin's
C++ code considers to be the cache directory is such a directory,
so there's no longer any reason to override the default path.
progressMessage can have the invalid value of 0. That
progressMessage was being used for the thread name was
a typo anyway – it's supposed to use progressTitle.
This reduces the build time for incremental builds from about
2 minutes to about 20 seconds. Most people never run the
unit tests on Android anyway (I'm not aware of anyone other
than me ever having done it).
I was going to rename this to getSysDirectory to make it clearer
what the returned path actually is, but it turns out we're not
actually using this for anything anymore.
The Java implementation of getting the list of post-processing
shaders only looked in the Sys folder and not the User folder.
This could be fixed in the Java implementation, but it's
simpler to just call the C++ implementation instead.
Center @2x and android banners, and adjust @1x banner to improve
consistency with other resolutions while maintaining sharpness.
Images created by MayImilae
In 5a1a642, I explicitly set android:debuggable="true" for Dolphin.
(By default, it's set for debug builds but not release builds.)
The reason I made the change is because debuggable must be set to
true in order to allow adb backup to be used with apps which target
Android 12. We have no reason to want to stop users from debugging
Dolphin and certainly no reason to stop users from using adb backup
(especially not after forced storage is going to force us to store
the User folder in app-specific external storage!), but,
it turns out that setting debuggable to true is forbidden by
Google Play "for security reasons". Go figure. The beta build
which was tagged earlier today was rejected because of this.
I think the AArch64 JIT has come far enough that it doesn't have to
be called experimental anymore.
I'm also labeling the x86-64 JIT as x86-64 for consistence with the
AArch64 JIT. This will especially be helpful if we start supporting
AArch64 on macOS, as AArch64 macOS can run both the x86-64 JIT and
the AArch64 JIT depending on whether you enable Rosetta 2.
We don't actually need to do this until we bump targetSdkVersion
to Android 12 (which we can't do yet since we're not compatible
with scoped storage), but I figured I'd get it out of the way early.
Not tested on Android 12, but tested to not break stuff on Android 10.
Many Android users want to disable SyncOnSkipIdle as a performance
hack, to the point where it's often suggested as something to
paste into Dolphin.ini (if not to use a fork). If adding it as
a setting in the GUI gives us an opportunity to explain what the
setting actually does and stops people from pasting stuff they
don't understand into INI files, I think it can be worth adding
despite how it can make games unstable. It not being in the GUI
doesn't seem to be stopping people from disabling it anyway.
The added setting in the GUI is a three-way setting called
"Synchronize GPU Thread" with the following alternatives:
"Never": SyncGPU = False, SyncOnIdleSkip = False
"On Idle Skipping": SyncGPU = False, SyncOnIdleSkip = True
"Always": SyncGPU = True, SyncOnIdleSkip = True
See PR 8203 for background on the game INI deletion prompt.
It's been almost two years since PR 8203 was merged, so you
would think that people are no longer creating game INIs that
contain a copy of every global setting, right? Unfortunately,
MMJ was forked not too long before that and never backported the
change, so right now there's a not insignificant number of people
online posting game INIs full of this garbage for others to use.
One thing that's been missing from the game INI deletion prompt
is a description of what the problem with having tons of extra
lines in a game INI actually is. This change adds that, in the
the hope that it will make people ignore the warning less often.
This option does in fact not enable and disable logging as a whole.
You can get logs through logcat regardless of this setting.
Also taking the opportunity to remove the reference to
the "dolphin-emu" folder name since we will no longer be
using that folder once scoped storage is applied to Dolphin.
GameFileCacheService.startRescan (in MainPresenter.onResume)
does nothing if called before GameFileCacheService.startLoad.
Fixes a 3f71c36 regression where already added games would not
show up after app launch under specific circumstances.
Unfortunately the loading indicator still doesn't show up
during a rescan initiated by app launch, but that would
be more annoying to fix, so I will leave it for now.
We want to save the currently selected platform tab,
but doing so immediately after switching tabs leads to
unnecessarily much file I/O (on the main thread, no less).
This change makes us defer the saving until later.
In master, the game scanning process looks like this:
1. Scan for games
2. Scan for additional metadata (icon.png and meta.xml)
3. Save the cache if needed
4. Update the game list with the results
This change makes the game scanning process look like this:
1. Scan for games
2. Update the game list with the results
3. Scan for additional metadata (icon.png and meta.xml)
4. Update the game list with the results
5. Save the cache if needed
Updating the game list as soon as possible means the user
has to wait less before their games show up. The new behavior
matches what DolphinWX did before it was removed. (DolphinQt
has an even fancier approach where games get added one by one.)
The main reason why I'm adding this isn't actually to allow
users to swipe down to refresh, it's to add a loading indicator.
Considering that the Storage Access Framework can be slow for
folders with many items (many subfolders?), not showing a
loading indicator might give users the impression that adding
a folder resulted in nothing happening even though Dolphin is
scanning for games in the background. But I suppose letting
users swipe down to refresh is a nice bonus with the change.
Added Opacity controls for the user to customize the opacity of their touchscreen controls. Also, placed both Scale and Opacity settings into one window/option called Adjust Controls.
In 8c723d0, I intended to update the main activity, emulation
activity and game properties dialog, but I forgot to actually
update the game properties dialog. This commit fixes that.
The changes outside of GamePropertiesDialog.java are just
to hide the Wii controller settings for GameCube games.
Basically, instead of having one button for config, one button
for graphics settings and so on, we now have just one settings
button which takes you to a screen where you pick between
config/graphics/GameCube controllers/Wii Remotes.
The main reason I want to do this is because people still have
trouble finding Overlay Controls in the "new" in-game menu.
Typically (depending on the screen size and the length of the
game name), the scrollable part of the menu can fit 4 items,
and merging Config and Graphics Settings into one item would
move Overlay Controls from 5th place to 4th place (assuming the
user doesn't have savestates enabled), which makes it findable
even for users who don't realize the menu can be scrolled.
The dialog that's shown when long pressing a game in the game
list is also shortened. While not a pressing matter, I think
it was getting a bit long.
An additional reason to do this is because we probably will
want to make it possible to edit the controller settings
from the in-game menu at some point in the future. With the
old approach, this would require us to dedicate a whopping 4
menu items just for settings (not including Overlay Controls),
which I think is excessive.
I moved it from the main settings screen to the in-game menu
in PR 8439 so that it could be changed while a game is running,
but now that the main settings can be accessed while a game is
running, there's no reason to not put it in the main settings.
https://bugs.dolphin-emu.org/issues/12067
Time for yet another new iteration of working around the
"surface destruction during boot" problem...
This time, the strategy is to use a mutex in MainAndroid.cpp.
This assumption is false both in portrait mode (where it only
covers the top half of the screen) and when using two apps at once.
Fixes https://bugs.dolphin-emu.org/issues/12307.
This was removed in cc5802b when it should have
been converted to the new setting instead,
and it seems like I didn't notice when reviewing it.
I'm changing the DirectoryInitialization logic a little
so that clearWiimoteNewIniLinkedPreferences also is called
when there was no WiimoteNew.ini previously, in case the
user deleted WiimoteNew.ini but not Dolphin.ini.
In case someone wants to be very careful with how much bandwidth
they use or with what data GameTDB.com collects on you.
This is already an option in DolphinQt (though in DolphinQt it
will switch entirely from using covers to banners when turned off).
It would be difficult to use the AlertMessage class for
messages that need to be showed outside of emulation,
but showing them as toasts is better than not showing them.
During emulation, when LocalGame has a value but CurrentRun
doesn't, we want to read from LocalGame, not CurrentRun. This
change exposes a LAYER_ACTIVE option that handles this correctly.
This is part of my efforts to add support for scoped storage.
I figured I would start with a relatively simple feature to
make sure that everyone is fine with the approach I'm taking
before I tackle more complicated features like the game list.
The only place where initTouchPointer was called automatically
was Host_RequestRenderWindowSize, which is called at least once
after emulation start, but not after activity recreation.
I was hoping we would be able to pull in the default values
from C++, but it seems like more trouble than it's worth,
partially because of different settings having default values
of different types and partially because we don't have any
convenient way to get a list of all C++ settings.
Except controller settings, because those would be annoying
to fit into the same system, and I only need the non-controller
settings to be brought over for the next commits to work.
...instead of waiting for it after launching EmulationActivity.
We need this because there is code that runs very early in
EmulationActivity that accesses the settings.
Android TV devices aren't the only devices without touchscreens.
Regarding MotionAlertDialog, I could've replaced the leanback
check with a touchscreen check instead of just removing it,
but I thought there was no reason to prevent people with
touchscreens from doing a long back press if they want to.
https://bugs.dolphin-emu.org/issues/12029
We currently have one way of opening the menu on touch screen
devices (swiping down from the top of the screen to bring up the
action bar and selecting the menu in the action bar), and another
way of opening the menu on Android TV (pressing Back). However,
some devices that claim to support touch (or don't support
leanback? Dolphin currently conflates the two) don't actually let
you swipe down from the top of the screen in the way that Dolphin
expects, notably Chromebooks. There are also some phones where you
can swipe down from the top of the screen but this for some reason
doesn't lead to the action bar becoming visible, though we are
getting less reports about this nowadays than in the past.
This change makes us use the Back method on all devices,
since it should work on all devices with no significant drawbacks.
Unfortunately, we not only have two different ways of triggering
the menu but actually two entirely different menus, with the
non-touch menu not implementing options that only are revelant
when using a touch screen. A later commit will add the missing
features to the menu that we now use on all devices.
The reason why the finish() call was added no longer exists.
(Also, there was never a duplicate SettingsActivity as far as
I can tell, only a duplicate SettingsFragment.)
Just for maintainability. This is a shorter and more standard
solution compared to our current one where the Fragment
persists the Settings and passes it to the Activity.
That a device doesn't have a touchscreen doesn't necessarily mean
that it doesn't support rumble (though it is usually the case).
setPhoneVibrator already contains a check for whether the device
supports rumble, so we can simply remove the touchscreen check.
Since updating to 28 took us so long that Google Play started
requiring updates to target 28 before we actually merged the PR that
made us target 28, I'm trying to get the update to 29 done early.
Setting targetSdkVersion to 28 would normally force scoped storage
on us, which we do not support yet. However, we can easily
avoid this by setting android:requestLegacyExternalStorage="true".
There will be no such luxury with targetSdkVersion 30, however...
This essentially reverts d9c78d5. Thanks to the previous commit,
the touch controls issue which was fixed by d9c78d5 is still fixed.
The behavior for gamepads is reverted, bringing back the ability
to run diagonally in games that had trouble with it after d9c78d5.
Currently, the touch controller overlay uses a square gate for
sticks. This commit changes that so that it instead uses the
stick gate configured in the INI, which ensures that the values
sent to the core are appropriately scaled regardless of what
is configured in the INI and makes the overlay look nicer
if the INI is set to a stick gate that matches the graphics.
While having motion control emulation of IR enabled by default
makes sense in situations like using a DualShock 4 on a PC,
Android has the additional option of touch emulation of IR
which seems to be better liked, and the default value which
was chosen with PC in mind was carried over to Android
without any particular consideration. This change disables
motion control emulation of IR by default on Android only.
Also fixes the same crash when accessing the game details
(which only can be accessed after long pressing a game).
The problem was that we were not using a theme that had
an AppCompat theme as a parent.
Unfortunately, the game details dialog uses white on white on
Android TV, and I don't know how to fix this in a clean way.
This fixes a crash on ATV devices, because the the AlertDialog is
from the appcompat class, but the theme derived from the parent
view on ATV devices isn't from AppCompat.