1- // This file is part of Keepass2Android, Copyright 2025 Philipp Crocoll.
2- //
3- // Keepass2Android is free software: you can redistribute it and/or modify
4- // it under the terms of the GNU General Public License as published by
5- // the Free Software Foundation, either version 3 of the License, or
6- // (at your option) any later version.
7- //
8- // Keepass2Android is distributed in the hope that it will be useful,
9- // but WITHOUT ANY WARRANTY; without even the implied warranty of
10- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11- // GNU General Public License for more details.
12- //
13- // You should have received a copy of the GNU General Public License
14- // along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
15-
1+ // This file is part of Keepass2Android, Copyright 2025 Philipp Crocoll.
2+ //
3+ // Keepass2Android is free software: you can redistribute it and/or modify
4+ // it under the terms of the GNU General Public License as published by
5+ // the Free Software Foundation, either version 3 of the License, or
6+ // (at your option) any later version.
7+ //
8+ // Keepass2Android is distributed in the hope that it will be useful,
9+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ // GNU General Public License for more details.
12+ //
13+ // You should have received a copy of the GNU General Public License
14+ // along with Keepass2Android. If not, see <http://www.gnu.org/licenses/>.
15+
1616using System ;
1717using System . Globalization ;
1818using Android . App ;
@@ -1081,6 +1081,93 @@ public bool GroupIsFound(GroupBaseActivity groupBaseActivity)
10811081 }
10821082 }
10831083
1084+ /// <summary>
1085+ /// Restores the full activity back stack (parent groups + EntryActivity) for
1086+ /// the last entry that was on screen when the database was locked by timeout.
1087+ /// Falls back silently to the root group if anything goes wrong.
1088+ /// </summary>
1089+ public class OpenLastEntryTask : AppTask
1090+ {
1091+ private readonly string _entryFullId ;
1092+
1093+ public OpenLastEntryTask ( string entryFullId )
1094+ {
1095+ _entryFullId = entryFullId ;
1096+ }
1097+
1098+ public override void LaunchFirstGroupActivity ( Activity act )
1099+ {
1100+ try
1101+ {
1102+ if ( string . IsNullOrEmpty ( _entryFullId ) )
1103+ {
1104+ FallBack ( act ) ;
1105+ return ;
1106+ }
1107+
1108+ var fullId = new ElementAndDatabaseId ( _entryFullId ) ;
1109+ var db = App . Kp2a . GetDatabase ( fullId . DatabaseId ) ;
1110+ if ( db == null )
1111+ {
1112+ FallBack ( act ) ;
1113+ return ;
1114+ }
1115+
1116+ var uuid = new PwUuid ( MemUtil . HexStringToByteArray ( fullId . ElementIdString ) ) ;
1117+ if ( ! db . EntriesById . ContainsKey ( uuid ) )
1118+ {
1119+ FallBack ( act ) ;
1120+ return ;
1121+ }
1122+
1123+ var entry = db . EntriesById [ uuid ] ;
1124+
1125+ // Build group chain from root down to the entry's immediate parent.
1126+ var groupChain = new System . Collections . Generic . List < PwGroup > ( ) ;
1127+ var current = entry . ParentGroup ;
1128+ while ( current != null )
1129+ {
1130+ groupChain . Insert ( 0 , current ) ;
1131+ current = current . ParentGroup ;
1132+ }
1133+
1134+ // Verify every group still exists in the database (handles moved/deleted entries).
1135+ foreach ( var g in groupChain )
1136+ {
1137+ if ( ! db . GroupsById . ContainsKey ( g . Uuid ) )
1138+ {
1139+ FallBack ( act ) ;
1140+ return ;
1141+ }
1142+ }
1143+
1144+ if ( App . Kp2a . CurrentDb != db )
1145+ App . Kp2a . CurrentDb = db ;
1146+
1147+ var nullTask = new NullTask ( ) ;
1148+ var stackBuilder = AndroidX . Core . App . TaskStackBuilder . Create ( act ) ;
1149+
1150+ foreach ( var group in groupChain )
1151+ stackBuilder . AddNextIntent ( GroupActivity . GetLaunchIntent ( act , group , nullTask ) ) ;
1152+
1153+ stackBuilder . AddNextIntent ( EntryActivity . GetLaunchIntent ( act , db , entry , 0 , nullTask ) ) ;
1154+
1155+ stackBuilder . StartActivities ( ) ;
1156+ act . Finish ( ) ;
1157+ }
1158+ catch ( Exception e )
1159+ {
1160+ Kp2aLog . LogUnexpectedError ( e ) ;
1161+ FallBack ( act ) ;
1162+ }
1163+ }
1164+
1165+ private static void FallBack ( Activity act )
1166+ {
1167+ GroupActivity . Launch ( act , new NullTask ( ) , new ActivityLaunchModeRequestCode ( 0 ) ) ;
1168+ }
1169+ }
1170+
10841171 public class NavigateToFolder : NavigateAndLaunchTask
10851172 {
10861173
@@ -1097,6 +1184,106 @@ public NavigateToFolder(Database db, PwGroup groups, bool toastEnable = false)
10971184
10981185 }
10991186
1187+ /// <summary>
1188+ /// When reached in a group activity, opens a specific entry by its FullId.
1189+ /// Used as the inner task of NavigateAndOpenEntryTask.
1190+ /// </summary>
1191+ public class OpenEntryTask : AppTask
1192+ {
1193+ public const string EntryFullIdKey = "OpenEntryFullId" ;
1194+ public string EntryFullId { get ; set ; }
1195+
1196+ public override void Setup ( Bundle b )
1197+ {
1198+ base . Setup ( b ) ;
1199+ EntryFullId = b . GetString ( EntryFullIdKey ) ;
1200+ }
1201+
1202+ public override IEnumerable < IExtra > Extras
1203+ {
1204+ get
1205+ {
1206+ foreach ( var e in base . Extras ) yield return e ;
1207+ if ( EntryFullId != null )
1208+ yield return new StringExtra { Key = EntryFullIdKey , Value = EntryFullId } ;
1209+ }
1210+ }
1211+
1212+ public override void StartInGroupActivity ( GroupBaseActivity groupBaseActivity )
1213+ {
1214+ if ( string . IsNullOrEmpty ( EntryFullId ) ) return ;
1215+ try
1216+ {
1217+ var id = new ElementAndDatabaseId ( EntryFullId ) ;
1218+ var uuid = new PwUuid ( MemUtil . HexStringToByteArray ( id . ElementIdString ) ) ;
1219+ var db = App . Kp2a . CurrentDb ;
1220+ if ( db == null || ! db . EntriesById . ContainsKey ( uuid ) ) return ;
1221+ groupBaseActivity . LaunchActivityForEntry ( db . EntriesById [ uuid ] , 0 ) ;
1222+ }
1223+ catch ( Exception e )
1224+ {
1225+ Kp2aLog . LogUnexpectedError ( e ) ;
1226+ }
1227+ }
1228+ }
1229+
1230+ /// <summary>
1231+ /// Navigates through the group hierarchy to the parent of a specific entry,
1232+ /// then opens that entry.
1233+ /// </summary>
1234+ public class NavigateAndOpenEntryTask : NavigateAndLaunchTask
1235+ {
1236+ public NavigateAndOpenEntryTask ( )
1237+ {
1238+ }
1239+
1240+ public NavigateAndOpenEntryTask ( PwGroup targetGroup , string entryFullId )
1241+ : base ( targetGroup , new OpenEntryTask { EntryFullId = entryFullId } )
1242+ {
1243+ }
1244+
1245+ public override void Setup ( Bundle b )
1246+ {
1247+ base . Setup ( b ) ;
1248+ TaskToBeLaunchedAfterNavigation = new OpenEntryTask ( ) ;
1249+ TaskToBeLaunchedAfterNavigation . Setup ( b ) ;
1250+ }
1251+ }
1252+
1253+ /// <summary>
1254+ /// Restores the last opened entry (from App.Kp2a.LastOpenedEntryFullIdForRestore)
1255+ /// by navigating through the group hierarchy and reopening the entry.
1256+ /// Falls back to the root group if the entry or its parent groups can no longer be found.
1257+ /// </summary>
1258+ public class RestoreLastEntryTask : AppTask
1259+ {
1260+ public override void LaunchFirstGroupActivity ( Activity act )
1261+ {
1262+ try
1263+ {
1264+ string fullId = App . Kp2a . LastOpenedEntryFullIdForRestore ;
1265+ if ( string . IsNullOrEmpty ( fullId ) ) { new NullTask ( ) . LaunchFirstGroupActivity ( act ) ; return ; }
1266+
1267+ var id = new ElementAndDatabaseId ( fullId ) ;
1268+ var db = App . Kp2a . GetDatabase ( id . DatabaseId ) ;
1269+ if ( db == null ) { new NullTask ( ) . LaunchFirstGroupActivity ( act ) ; return ; }
1270+
1271+ var uuid = new PwUuid ( MemUtil . HexStringToByteArray ( id . ElementIdString ) ) ;
1272+ if ( ! db . EntriesById . ContainsKey ( uuid ) ) { new NullTask ( ) . LaunchFirstGroupActivity ( act ) ; return ; }
1273+
1274+ var entry = db . EntriesById [ uuid ] ;
1275+ if ( entry . ParentGroup == null ) { new NullTask ( ) . LaunchFirstGroupActivity ( act ) ; return ; }
1276+
1277+ new NavigateAndOpenEntryTask ( entry . ParentGroup , fullId ) . LaunchFirstGroupActivity ( act ) ;
1278+ }
1279+ catch ( Exception e )
1280+ {
1281+ Kp2aLog . LogUnexpectedError ( e ) ;
1282+ new NullTask ( ) . LaunchFirstGroupActivity ( act ) ;
1283+ }
1284+ }
1285+ }
1286+
11001287 public class NavigateToFolderAndLaunchMoveElementTask : NavigateAndLaunchTask
11011288 {
11021289
0 commit comments