app shortcuts (WIP)
This commit is contained in:
parent
0c06bf8443
commit
9da57961fc
8 changed files with 121 additions and 35 deletions
|
@ -45,19 +45,20 @@
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name="io.flutter.app.FlutterApplication"
|
android:name="io.flutter.app.FlutterApplication"
|
||||||
android:label="@string/app_name"
|
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
android:requestLegacyExternalStorage="true">
|
android:requestLegacyExternalStorage="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:launchMode="singleTop"
|
|
||||||
android:theme="@style/AppTheme"
|
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
|
android:theme="@style/AppTheme"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
|
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
|
||||||
android:value="false" />
|
android:value="false" />
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
@ -65,14 +66,17 @@
|
||||||
<intent-filter tools:ignore="AppLinkUrlError">
|
<intent-filter tools:ignore="AppLinkUrlError">
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="image/*" />
|
<data android:mimeType="image/*" />
|
||||||
<data android:mimeType="video/*" />
|
<data android:mimeType="video/*" />
|
||||||
<data android:mimeType="vnd.android.cursor.dir/image" />
|
<data android:mimeType="vnd.android.cursor.dir/image" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.GET_CONTENT" />
|
<action android:name="android.intent.action.GET_CONTENT" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.OPENABLE" />
|
<category android:name="android.intent.category.OPENABLE" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="image/*" />
|
<data android:mimeType="image/*" />
|
||||||
<data android:mimeType="video/*" />
|
<data android:mimeType="video/*" />
|
||||||
<data android:mimeType="vnd.android.cursor.dir/image" />
|
<data android:mimeType="vnd.android.cursor.dir/image" />
|
||||||
|
@ -80,6 +84,7 @@
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.PICK" />
|
<action android:name="android.intent.action.PICK" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<data android:mimeType="image/*" />
|
<data android:mimeType="image/*" />
|
||||||
<data android:mimeType="video/*" />
|
<data android:mimeType="video/*" />
|
||||||
<data android:mimeType="vnd.android.cursor.dir/image" />
|
<data android:mimeType="vnd.android.cursor.dir/image" />
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
package deckers.thibault.aves;
|
package deckers.thibault.aves;
|
||||||
|
|
||||||
import android.content.Intent;
|
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.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -29,7 +37,7 @@ public class MainActivity extends FlutterActivity {
|
||||||
|
|
||||||
public static final String VIEWER_CHANNEL = "deckers.thibault/aves/viewer";
|
public static final String VIEWER_CHANNEL = "deckers.thibault/aves/viewer";
|
||||||
|
|
||||||
private Map<String, String> intentDataMap;
|
private Map<String, Object> intentDataMap;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -69,6 +77,33 @@ public class MainActivity extends FlutterActivity {
|
||||||
finish();
|
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) {
|
private void handleIntent(Intent intent) {
|
||||||
|
@ -77,6 +112,15 @@ public class MainActivity extends FlutterActivity {
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
if (action == null) return;
|
if (action == null) return;
|
||||||
switch (action) {
|
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:
|
case Intent.ACTION_VIEW:
|
||||||
Uri uri = intent.getData();
|
Uri uri = intent.getData();
|
||||||
String mimeType = intent.getType();
|
String mimeType = intent.getType();
|
||||||
|
|
5
android/app/src/main/res/drawable/ic_outline_movie.xml
Normal file
5
android/app/src/main/res/drawable/ic_outline_movie.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<vector android:autoMirrored="true" android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal" android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M4,6.47L5.76,10H20v8H4V6.47M22,4h-4l2,4h-3l-2,-4h-2l2,4h-3l-2,-4H8l2,4H7L5,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4z"/>
|
||||||
|
</vector>
|
5
android/app/src/main/res/drawable/ic_outline_search.xml
Normal file
5
android/app/src/main/res/drawable/ic_outline_search.xml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
<vector android:autoMirrored="true" android:height="24dp"
|
||||||
|
android:tint="?attr/colorControlNormal" android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||||
|
</vector>
|
|
@ -1,4 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Aves</string>
|
<string name="app_name">Aves</string>
|
||||||
|
<string name="search_shortcut_short_label">Search</string>
|
||||||
|
<string name="videos_shortcut_short_label">Videos</string>
|
||||||
</resources>
|
</resources>
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:aves/widgets/collection/collection_page.dart';
|
import 'package:aves/widgets/collection/collection_page.dart';
|
||||||
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
||||||
|
|
||||||
enum HomePageSetting { collection, albums }
|
enum HomePageSetting { collection, albums, search }
|
||||||
|
|
||||||
extension ExtraHomePageSetting on HomePageSetting {
|
extension ExtraHomePageSetting on HomePageSetting {
|
||||||
String get name {
|
String get name {
|
||||||
|
@ -10,6 +10,8 @@ extension ExtraHomePageSetting on HomePageSetting {
|
||||||
return 'Collection';
|
return 'Collection';
|
||||||
case HomePageSetting.albums:
|
case HomePageSetting.albums:
|
||||||
return 'Albums';
|
return 'Albums';
|
||||||
|
case HomePageSetting.search:
|
||||||
|
return 'Search';
|
||||||
default:
|
default:
|
||||||
return toString();
|
return toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
Widget _buildAppBarTitle() {
|
Widget _buildAppBarTitle() {
|
||||||
if (collection.isBrowsing) {
|
if (collection.isBrowsing) {
|
||||||
Widget title = Text(
|
Widget title = Text(
|
||||||
AvesApp.mode == AppMode.pick ? 'Select' : 'Collection',
|
AvesApp.mode == AppMode.pick ? 'Pick' : 'Collection',
|
||||||
key: Key('appbar-title'),
|
key: Key('appbar-title'),
|
||||||
);
|
);
|
||||||
if (AvesApp.mode == AppMode.main) {
|
if (AvesApp.mode == AppMode.main) {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import 'package:aves/main.dart';
|
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/image_entry.dart';
|
||||||
|
import 'package:aves/model/mime_types.dart';
|
||||||
import 'package:aves/model/settings/home_page.dart';
|
import 'package:aves/model/settings/home_page.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/model/source/collection_lens.dart';
|
import 'package:aves/model/source/collection_lens.dart';
|
||||||
|
@ -29,6 +32,8 @@ class HomePage extends StatefulWidget {
|
||||||
class _HomePageState extends State<HomePage> {
|
class _HomePageState extends State<HomePage> {
|
||||||
MediaStoreSource _mediaStore;
|
MediaStoreSource _mediaStore;
|
||||||
ImageEntry _viewerEntry;
|
ImageEntry _viewerEntry;
|
||||||
|
HomePageSetting _shortcutPage;
|
||||||
|
List<String> _shortcutFilters;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -77,6 +82,11 @@ class _HomePageState extends State<HomePage> {
|
||||||
String pickMimeTypes = intentData['mimeType'];
|
String pickMimeTypes = intentData['mimeType'];
|
||||||
debugPrint('pick mimeType=$pickMimeTypes');
|
debugPrint('pick mimeType=$pickMimeTypes');
|
||||||
break;
|
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<String>() : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,27 +110,43 @@ class _HomePageState extends State<HomePage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Route _getRedirectRoute() {
|
Route _getRedirectRoute() {
|
||||||
switch (AvesApp.mode) {
|
if (AvesApp.mode == AppMode.view) {
|
||||||
case AppMode.view:
|
|
||||||
return DirectMaterialPageRoute(
|
return DirectMaterialPageRoute(
|
||||||
settings: RouteSettings(name: SingleFullscreenPage.routeName),
|
settings: RouteSettings(name: SingleFullscreenPage.routeName),
|
||||||
builder: (_) => SingleFullscreenPage(entry: _viewerEntry),
|
builder: (_) => SingleFullscreenPage(entry: _viewerEntry),
|
||||||
);
|
);
|
||||||
case AppMode.main:
|
}
|
||||||
case AppMode.pick:
|
|
||||||
if (_mediaStore != null) {
|
HomePageSetting startPage;
|
||||||
switch (settings.homePage) {
|
Iterable<CollectionFilter> 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:
|
case HomePageSetting.albums:
|
||||||
return DirectMaterialPageRoute(
|
return DirectMaterialPageRoute(
|
||||||
settings: RouteSettings(name: AlbumListPage.routeName),
|
settings: RouteSettings(name: AlbumListPage.routeName),
|
||||||
builder: (_) => AlbumListPage(source: _mediaStore),
|
builder: (_) => AlbumListPage(source: _mediaStore),
|
||||||
);
|
);
|
||||||
|
case HomePageSetting.search:
|
||||||
case HomePageSetting.collection:
|
case HomePageSetting.collection:
|
||||||
|
default:
|
||||||
return DirectMaterialPageRoute(
|
return DirectMaterialPageRoute(
|
||||||
settings: RouteSettings(name: CollectionPage.routeName),
|
settings: RouteSettings(name: CollectionPage.routeName),
|
||||||
builder: (_) => CollectionPage(
|
builder: (_) => CollectionPage(
|
||||||
CollectionLens(
|
CollectionLens(
|
||||||
source: _mediaStore,
|
source: _mediaStore,
|
||||||
|
filters: filters,
|
||||||
groupFactor: settings.collectionGroupFactor,
|
groupFactor: settings.collectionGroupFactor,
|
||||||
sortFactor: settings.collectionSortFactor,
|
sortFactor: settings.collectionSortFactor,
|
||||||
),
|
),
|
||||||
|
@ -128,7 +154,4 @@ class _HomePageState extends State<HomePage> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue