From 6857bc32a8c60cded26930de27b3e998a60881d0 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sat, 31 Aug 2019 01:48:59 -0400 Subject: [PATCH] android: frontend: Implement persistent notification while emulator is running. --- .../citra/citra_android/CitraApplication.java | 24 ++++++++++++++- .../activities/EmulationActivity.java | 29 ++++++++++++++++++ .../citra_android/ui/main/MainActivity.java | 11 ++++++- .../ic_stat_notification_logo.png | Bin 0 -> 2824 bytes .../ic_stat_notification_logo.png | Bin 0 -> 4026 bytes .../ic_stat_notification_logo.png | Bin 0 -> 5936 bytes .../app/src/main/res/values/strings.xml | 4 +++ 7 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 src/android/app/src/main/res/drawable-hdpi/ic_stat_notification_logo.png create mode 100644 src/android/app/src/main/res/drawable-xhdpi/ic_stat_notification_logo.png create mode 100644 src/android/app/src/main/res/drawable-xxhdpi/ic_stat_notification_logo.png diff --git a/src/android/app/src/main/java/org/citra/citra_android/CitraApplication.java b/src/android/app/src/main/java/org/citra/citra_android/CitraApplication.java index 9318ff4b0..29cffebc5 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/CitraApplication.java +++ b/src/android/app/src/main/java/org/citra/citra_android/CitraApplication.java @@ -5,7 +5,10 @@ package org.citra.citra_android; import android.app.Application; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.content.Context; +import android.os.Build; import org.citra.citra_android.model.GameDatabase; import org.citra.citra_android.services.DirectoryInitializationService; @@ -15,13 +18,32 @@ public class CitraApplication extends Application { public static GameDatabase databaseHelper; private static CitraApplication application; + private void createNotificationChannel() { + // Create the NotificationChannel, but only on API 26+ because + // the NotificationChannel class is new and not in the support library + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + CharSequence name = getString(R.string.app_notification_channel_name); + String description = getString(R.string.app_notification_channel_description); + int importance = NotificationManager.IMPORTANCE_DEFAULT; + NotificationChannel channel = new NotificationChannel(getString(R.string.app_notification_channel_id), name, importance); + channel.setDescription(description); + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + NotificationManager notificationManager = getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(channel); + } + } + @Override public void onCreate() { super.onCreate(); application = this; - if (PermissionsHandler.hasWriteAccess(getApplicationContext())) + if (PermissionsHandler.hasWriteAccess(getApplicationContext())) { DirectoryInitializationService.startService(getApplicationContext()); + } + + createNotificationChannel(); databaseHelper = new GameDatabase(this); } diff --git a/src/android/app/src/main/java/org/citra/citra_android/activities/EmulationActivity.java b/src/android/app/src/main/java/org/citra/citra_android/activities/EmulationActivity.java index db20ec676..f9f90a03a 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/activities/EmulationActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_android/activities/EmulationActivity.java @@ -12,6 +12,8 @@ import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; +import android.support.v4.app.NotificationCompat; +import android.support.v4.app.NotificationManagerCompat; import android.support.v7.app.AppCompatActivity; import android.util.SparseIntArray; import android.view.InputDevice; @@ -63,6 +65,8 @@ public final class EmulationActivity extends AppCompatActivity { public static final int MENU_ACTION_SCREEN_LAYOUT_SIDEBYSIDE = 8; public static final int MENU_ACTION_SWAP_SCREENS = 9; public static final int MENU_ACTION_RESET_OVERLAY = 10; + + private static final int EMULATION_RUNNING_NOTIFICATION = 0x1000; private static final String BACKSTACK_NAME_MENU = "menu"; private static final String BACKSTACK_NAME_SUBMENU = "submenu"; private static SparseIntArray buttonsActionsMap = new SparseIntArray(); @@ -132,6 +136,27 @@ public final class EmulationActivity extends AppCompatActivity { options.toBundle()); } + private void showRunningNotification() { + NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.app_notification_channel_id)) + .setSmallIcon(R.drawable.ic_stat_notification_logo) + .setContentTitle(getString(R.string.app_name)) + .setContentText(getString(R.string.app_notification_running)) + .setPriority(NotificationCompat.PRIORITY_MIN) + .setOngoing(true); + + NotificationManagerCompat.from(this).notify(EMULATION_RUNNING_NOTIFICATION, builder.build()); + } + + public static void tryDismissRunningNotification(Activity activity) { + NotificationManagerCompat.from(activity).cancel(EMULATION_RUNNING_NOTIFICATION); + } + + @Override + protected void onDestroy() { + tryDismissRunningNotification(this); + super.onDestroy(); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -221,6 +246,8 @@ public final class EmulationActivity extends AppCompatActivity { } mPreferences = PreferenceManager.getDefaultSharedPreferences(this); + + showRunningNotification(); } @Override @@ -308,6 +335,8 @@ public final class EmulationActivity extends AppCompatActivity { } public void exitWithAnimation() { + tryDismissRunningNotification(this); + runOnUiThread(() -> { Picasso.get() diff --git a/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainActivity.java b/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainActivity.java index b66d35fd4..176b825b7 100644 --- a/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainActivity.java +++ b/src/android/app/src/main/java/org/citra/citra_android/ui/main/MainActivity.java @@ -6,7 +6,6 @@ import android.database.Cursor; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; -import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Menu; @@ -15,6 +14,7 @@ import android.view.MenuItem; import android.widget.Toast; import org.citra.citra_android.R; +import org.citra.citra_android.activities.EmulationActivity; import org.citra.citra_android.model.GameProvider; import org.citra.citra_android.services.DirectoryInitializationService; import org.citra.citra_android.ui.platform.PlatformGamesFragment; @@ -60,6 +60,9 @@ public final class MainActivity extends AppCompatActivity implements MainView { } else { mPlatformGamesFragment = (PlatformGamesFragment) getSupportFragmentManager().getFragment(savedInstanceState, "mPlatformGamesFragment"); } + + // Dismiss previous notifications (should not happen unless a crash occurred) + EmulationActivity.tryDismissRunningNotification(this); } @Override @@ -191,4 +194,10 @@ public final class MainActivity extends AppCompatActivity implements MainView { private PlatformGamesView getPlatformGamesView() { return (PlatformGamesView) getSupportFragmentManager().findFragmentById(mFrameLayoutId); } + + @Override + protected void onDestroy() { + EmulationActivity.tryDismissRunningNotification(this); + super.onDestroy(); + } } diff --git a/src/android/app/src/main/res/drawable-hdpi/ic_stat_notification_logo.png b/src/android/app/src/main/res/drawable-hdpi/ic_stat_notification_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2282f1a3b936767a009858111f1177dad3acc260 GIT binary patch literal 2824 zcmX|@c{~&TAIE2w`$&!v)tY0@jaiN{cV=eD9dd1pWkxI(k~ve(k&sk!B;{D;rXfcc zCFPC~I*5pn{Pg{OAK%a8@%g+z@6Y@Fc)lL5_g|kBdt0oqz(D~303eLBvT)doR)04i z&)(_0_z(*KaN#LVZge+-4VoAn1otHc`;p;{ppd;f0AOg$2=OHblIajXvOk4}fqrj( z0)DlH7nz(D`u zqWA89pAk^VKP~h?4Ak_mAjFMe4>1o8B}0zEbu@@tC?rHzAFidNsgFddLy($Ux(H2O zgqF63rWRUL2d%9S`FBB$1R#c?q|;~z3(J4S_A(4KfKCrVBM=c05%36YcyOpcLQ7v? zAEAjvAdwn-9U5WhX>?zP1}#kfFXI0U3vw7SloCRx1k)gYnZAC(=ja$H6!BLC@vq9B zJ;Zu}OarSF-8-ETdL%=)qMC2;1t-Bsb)*6~%sL_C_?k+}+--)1QC zivZ1I%{j=axE!NzAo!0zJc}-XNd$Tfgy9~k+={_%7aA~{9F0}FCU#ln5A_n$Gd5XG z$A2}t4Tx=cIJy}_uvnGx^a+@{zc1>!06B6sYW z`?_VvH9|CMMh&W-(uVW-=@C6*gJKNvh1_;6@TuA&7ZG>jwhsn#vB@{e%h?20UC5lc z)GDnd$bKe6|#G+Du^ltK9;a;?bjDcmxMS5TfV zd@@)3Ql4EX7pOh$Y*7Dl=rdI^S-kl9siRw1g&o%cJX><}0W0b5{wuZ~mZuw%-j$}l zBWcbN)2T~wC3S3>h@6=n$I(fZG2LcYoklvWu+$oXJ3?;ACHr<8-E7ZPz?5~^nqF0R zI*OA$G-mE3$7`nZMxED#ys}g$04uK)z~?_=D7~BtR1uQP+{bp0YT_oJFaXQ9HW?I# zVG7q-OG(`y2_wMwt+{<{*&Inx-e)q`?N=}--QEo~+YAisZ&Yj9^5F#L-PxWWq$Lxp zFY|waQZD4{l<(uemzF#9g!~ok9m6V#uozl7% zy@U9z`^4=D>K{DYz9QKFj7)e(NIgB;2CY}#aOzovs@FQb`)O~GgVttgca5?fD@fv( z&e97Xd69StvrO-*>_sibE)Ug~xtSW|>-S?Sqgm=P-!gp57k*KWA0D0>fi*}~@*L)B z%k^tzOTBo%Ao5$A`MZj9wjg%UQ2kpdz@zz9r_0Xy8PT4YiV#?j+77+NcD*6mGVx`a z-fyoi%t9In{gXutrz??{j|r!fV}Fetif8)cz$K%XyC|Uh#hHy_$!W})Bm-h z1M~FVlpk|Aqv|QS_G&k)x;n(cxf-ipj8^F<}&#U$M9`P=Z9qC@%0zs(b^yb7W_ zAbtE{Q2V4xN_PRIJ7II?u)6x~U$WRTtc-}~k-1pMd(M#@8nwm!dG%C^_ub5S$MR{a zhC*x6?G^ON!U_qk`CcQKUO{`f*MiKntFmbfqR}K#`L=;RK7NR#w&h^D{sHM1mx3px z9Z@azPY&9qu3PUvEB-K^BjCfITej`O-&hUQuX7|nsh)FP$_B)23+T!Kk(4s^(f_F9iau~&uiOtfZYR}(PG&u2yuI*iQju# zmwPdN(ajbX6C{!7#ZmN{xkfyB2nH?7jFnS1^t(|rUkj^Kj@~DDimDVXTaO9Jg-!A< zWL$7^AgpGnoy`;ZjjRX>5sH+Gl8gSmR@7>9s3$c-NkPH7VQ4&`+6_j}7(_?Th ze1^^Tk~D6n_V-M80WP?6AO+0TPrRO-mfo_9u|-f_!}sUTjLYID30LBWM{A@6ud8>q zc^VaFTz~dvc-MGAo%+Bf?LyC4r8+15eP1cbT1X?nmm0e^T`pOxT=aOH)T61{?p?sU Xi!REX(-EbAKd`vtwiZ=pK5_p811}ST literal 0 HcmV?d00001 diff --git a/src/android/app/src/main/res/drawable-xhdpi/ic_stat_notification_logo.png b/src/android/app/src/main/res/drawable-xhdpi/ic_stat_notification_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5e2787ba36b293bb89958c547475af1afc93405d GIT binary patch literal 4026 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 aPed{S4=#Srw6p(Xxly{tIu*AtQU3>{W>bj( literal 0 HcmV?d00001 diff --git a/src/android/app/src/main/res/drawable-xxhdpi/ic_stat_notification_logo.png b/src/android/app/src/main/res/drawable-xxhdpi/ic_stat_notification_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..06cef9de3a1ab7c667d7caefa294907f8e0269cd GIT binary patch literal 5936 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 Citra This software will run games for the Nintendo 3DS handheld game console. No game titles are included.\n\nBefore you run, please place your rightfully owned 3DS game files onto your device storage. + Citra + Citra + Citra 3DS emulator notifications + Citra is running Circle Pad