diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f1c539b32..c1717c39a 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -45,19 +45,20 @@ + @@ -65,14 +66,17 @@ + + + @@ -80,6 +84,7 @@ + diff --git a/android/app/src/main/java/deckers/thibault/aves/MainActivity.java b/android/app/src/main/java/deckers/thibault/aves/MainActivity.java index f248ce1cf..c5df45489 100644 --- a/android/app/src/main/java/deckers/thibault/aves/MainActivity.java +++ b/android/app/src/main/java/deckers/thibault/aves/MainActivity.java @@ -1,10 +1,18 @@ package deckers.thibault.aves; import android.content.Intent; +import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutManager; +import android.graphics.drawable.Icon; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.util.Log; +import androidx.annotation.RequiresApi; + +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Objects; @@ -29,7 +37,7 @@ public class MainActivity extends FlutterActivity { public static final String VIEWER_CHANNEL = "deckers.thibault/aves/viewer"; - private Map intentDataMap; + private Map intentDataMap; @Override protected void onCreate(Bundle savedInstanceState) { @@ -69,6 +77,33 @@ public class MainActivity extends FlutterActivity { finish(); } }); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { + setupShortcuts(); + } + } + + @RequiresApi(Build.VERSION_CODES.N_MR1) + private void setupShortcuts() { + ShortcutManager shortcutManager = getSystemService(ShortcutManager.class); + + Intent searchIntent = new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class); + searchIntent.putExtra("page", "search"); + ShortcutInfo search = new ShortcutInfo.Builder(this, "search") + .setShortLabel(getString(R.string.search_shortcut_short_label)) + .setIcon(Icon.createWithResource(this, R.drawable.ic_outline_search)) + .setIntent(searchIntent) + .build(); + + Intent videosIntent = new Intent(Intent.ACTION_MAIN, null, this, MainActivity.class); + videosIntent.putExtra("page", "collection"); + videosIntent.putExtra("filters", new String[]{"anyVideo"}); + ShortcutInfo videos = new ShortcutInfo.Builder(this, "videos") + .setShortLabel(getString(R.string.videos_shortcut_short_label)) + .setIcon(Icon.createWithResource(this, R.drawable.ic_outline_movie)) + .setIntent(videosIntent) + .build(); + shortcutManager.setDynamicShortcuts(Arrays.asList(videos, search)); } private void handleIntent(Intent intent) { @@ -77,6 +112,15 @@ public class MainActivity extends FlutterActivity { String action = intent.getAction(); if (action == null) return; switch (action) { + case Intent.ACTION_MAIN: + String page = intent.getStringExtra("page"); + if (page != null) { + intentDataMap = new HashMap<>(); + intentDataMap.put("page", page); + String[] filters = intent.getStringArrayExtra("filters"); + intentDataMap.put("filters", filters != null ? new ArrayList<>(Arrays.asList(filters)) : null); + } + break; case Intent.ACTION_VIEW: Uri uri = intent.getData(); String mimeType = intent.getType(); diff --git a/android/app/src/main/res/drawable/ic_outline_movie.xml b/android/app/src/main/res/drawable/ic_outline_movie.xml new file mode 100644 index 000000000..ca2e076e1 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_outline_movie.xml @@ -0,0 +1,5 @@ + + + diff --git a/android/app/src/main/res/drawable/ic_outline_search.xml b/android/app/src/main/res/drawable/ic_outline_search.xml new file mode 100644 index 000000000..029cb754c --- /dev/null +++ b/android/app/src/main/res/drawable/ic_outline_search.xml @@ -0,0 +1,5 @@ + + + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 53cda64da..c296a8709 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,4 +1,6 @@ Aves + Search + Videos \ No newline at end of file diff --git a/lib/model/settings/home_page.dart b/lib/model/settings/home_page.dart index bc5c29b75..480c4e896 100644 --- a/lib/model/settings/home_page.dart +++ b/lib/model/settings/home_page.dart @@ -1,7 +1,7 @@ import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart'; -enum HomePageSetting { collection, albums } +enum HomePageSetting { collection, albums, search } extension ExtraHomePageSetting on HomePageSetting { String get name { @@ -10,6 +10,8 @@ extension ExtraHomePageSetting on HomePageSetting { return 'Collection'; case HomePageSetting.albums: return 'Albums'; + case HomePageSetting.search: + return 'Search'; default: return toString(); } diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index 314f29ce5..8605d4636 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -135,7 +135,7 @@ class _CollectionAppBarState extends State with SingleTickerPr Widget _buildAppBarTitle() { if (collection.isBrowsing) { Widget title = Text( - AvesApp.mode == AppMode.pick ? 'Select' : 'Collection', + AvesApp.mode == AppMode.pick ? 'Pick' : 'Collection', key: Key('appbar-title'), ); if (AvesApp.mode == AppMode.main) { diff --git a/lib/widgets/home_page.dart b/lib/widgets/home_page.dart index 920c0b38d..936c64af4 100644 --- a/lib/widgets/home_page.dart +++ b/lib/widgets/home_page.dart @@ -1,5 +1,8 @@ import 'package:aves/main.dart'; +import 'package:aves/model/filters/filters.dart'; +import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/image_entry.dart'; +import 'package:aves/model/mime_types.dart'; import 'package:aves/model/settings/home_page.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; @@ -29,6 +32,8 @@ class HomePage extends StatefulWidget { class _HomePageState extends State { MediaStoreSource _mediaStore; ImageEntry _viewerEntry; + HomePageSetting _shortcutPage; + List _shortcutFilters; @override void initState() { @@ -77,6 +82,11 @@ class _HomePageState extends State { String pickMimeTypes = intentData['mimeType']; debugPrint('pick mimeType=$pickMimeTypes'); break; + default: + final extraPage = intentData['page']; + _shortcutPage = HomePageSetting.values.firstWhere((v) => v.toString().split('.')[1] == extraPage, orElse: () => null); + final extraFilters = intentData['filters']; + _shortcutFilters = extraFilters != null ? (extraFilters as List).cast() : null; } } @@ -100,35 +110,48 @@ class _HomePageState extends State { } Route _getRedirectRoute() { - switch (AvesApp.mode) { - case AppMode.view: - return DirectMaterialPageRoute( - settings: RouteSettings(name: SingleFullscreenPage.routeName), - builder: (_) => SingleFullscreenPage(entry: _viewerEntry), - ); - case AppMode.main: - case AppMode.pick: - if (_mediaStore != null) { - switch (settings.homePage) { - case HomePageSetting.albums: - return DirectMaterialPageRoute( - settings: RouteSettings(name: AlbumListPage.routeName), - builder: (_) => AlbumListPage(source: _mediaStore), - ); - case HomePageSetting.collection: - return DirectMaterialPageRoute( - settings: RouteSettings(name: CollectionPage.routeName), - builder: (_) => CollectionPage( - CollectionLens( - source: _mediaStore, - groupFactor: settings.collectionGroupFactor, - sortFactor: settings.collectionSortFactor, - ), - ), - ); - } - } + if (AvesApp.mode == AppMode.view) { + return DirectMaterialPageRoute( + settings: RouteSettings(name: SingleFullscreenPage.routeName), + builder: (_) => SingleFullscreenPage(entry: _viewerEntry), + ); + } + + HomePageSetting startPage; + Iterable filters; + if (AvesApp.mode == AppMode.pick) { + startPage = HomePageSetting.collection; + } else { + startPage = _shortcutPage ?? settings.homePage; + filters = (_shortcutFilters ?? []).map((filterString) { + switch (filterString) { + case 'anyVideo': + return MimeFilter(MimeTypes.anyVideo); + } + debugPrint('failed to parse shortcut filter=$filterString'); + return null; + }); + } + switch (startPage) { + case HomePageSetting.albums: + return DirectMaterialPageRoute( + settings: RouteSettings(name: AlbumListPage.routeName), + builder: (_) => AlbumListPage(source: _mediaStore), + ); + case HomePageSetting.search: + case HomePageSetting.collection: + default: + return DirectMaterialPageRoute( + settings: RouteSettings(name: CollectionPage.routeName), + builder: (_) => CollectionPage( + CollectionLens( + source: _mediaStore, + filters: filters, + groupFactor: settings.collectionGroupFactor, + sortFactor: settings.collectionSortFactor, + ), + ), + ); } - return null; } }