From 6516fe3cd8cc1bd6dc18c60d68e73fa3f3bbfb53 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 3 May 2016 20:42:30 +0100 Subject: [PATCH 01/10] Launch a file list for a plugin path --- .../xbmc/kore/ui/MediaFileListFragment.java | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java b/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java index 36fdf07..1df24a5 100644 --- a/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java @@ -52,6 +52,7 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; +import java.util.regex.Pattern; /** * Presents a list of files of different types (Video/Music) @@ -63,6 +64,7 @@ public class MediaFileListFragment extends AbstractListFragment { public static final String PATH_CONTENTS = "pathContents"; public static final String ROOT_PATH_CONTENTS = "rootPathContents"; public static final String ROOT_VISITED = "rootVisited"; + public static final String ROOT_PATH = "rootPath"; private static final String ADDON_SOURCE = "addons:"; private HostManager hostManager; @@ -125,8 +127,10 @@ public class MediaFileListFragment extends AbstractListFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = super.onCreateView(inflater, container, savedInstanceState); Bundle args = getArguments(); + FileLocation rootPath = null; if (args != null) { - mediaType = args.getString(MEDIA_TYPE, Files.Media.MUSIC); + rootPath = args.getParcelable(ROOT_PATH); + mediaType = args.getString(MEDIA_TYPE, Files.Media.FILES); if (mediaType.equalsIgnoreCase(Files.Media.VIDEO)) { playlistId = PlaylistType.VIDEO_PLAYLISTID; } else if (mediaType.equalsIgnoreCase(Files.Media.PICTURES)) { @@ -156,6 +160,10 @@ public class MediaFileListFragment extends AbstractListFragment { browseRootAlready = savedInstanceState.getBoolean(ROOT_VISITED); ((MediaFileListAdapter) getAdapter()).setFilelistItems(list); } + else if (rootPath != null) { + browseRootAlready = true; + browseDirectory(rootPath); + } else { browseSources(); } @@ -308,11 +316,12 @@ public class MediaFileListFragment extends AbstractListFragment { ArrayList flList = new ArrayList(); - // insert the parent directory as the first item in the list - FileLocation fl = new FileLocation("..", parentDirectory, true); - fl.setRootDir(dir.isRootDir()); - flList.add(0, fl); - + if (dir.hasParent) { + // insert the parent directory as the first item in the list + FileLocation fl = new FileLocation("..", parentDirectory, true); + fl.setRootDir(dir.isRootDir()); + flList.add(0, fl); + } for (ListType.ItemFile i : result) { flList.add(FileLocation.newInstanceFromItemFile(getActivity(), i)); } @@ -630,6 +639,7 @@ public class MediaFileListFragment extends AbstractListFragment { public final String file; public final boolean isDirectory; + public final boolean hasParent; private boolean isRoot; @@ -640,10 +650,13 @@ public class MediaFileListFragment extends AbstractListFragment { this(title, path, isDir, null, null, null); } + static final Pattern noParent = Pattern.compile("plugin://[^/]*/?"); public FileLocation(String title, String path, boolean isDir, String details, String sizeDuration, String artUrl) { this.title = title; this.file = path; this.isDirectory = isDir; + this.hasParent = !noParent.matcher(path).matches(); + this.isRoot = false; this.details = details; @@ -698,6 +711,7 @@ public class MediaFileListFragment extends AbstractListFragment { this.title = in.readString(); this.file = in.readString(); this.isDirectory = (in.readInt() != 0); + this.hasParent = (in.readInt() != 0); this.isRoot = (in.readInt() != 0); this.details = in.readString(); @@ -713,6 +727,7 @@ public class MediaFileListFragment extends AbstractListFragment { out.writeString(title); out.writeString(file); out.writeInt(isDirectory ? 1 : 0); + out.writeInt(hasParent ? 1 : 0); out.writeInt(isRoot ? 1 : 0); out.writeString(details); From dadc34901cc091e411e8d8d387106f2d2ca41204 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 3 May 2016 20:58:12 +0100 Subject: [PATCH 02/10] Plugin content browser For feature request #72 --- .../xbmc/kore/ui/AddonOverviewFragment.java | 137 ++++++++++++++++++ .../java/org/xbmc/kore/ui/AddonsActivity.java | 2 +- app/src/main/res/values/strings.xml | 3 + 3 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java b/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java new file mode 100644 index 0000000..a8f3425 --- /dev/null +++ b/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java @@ -0,0 +1,137 @@ +/* + * Copyright 2016 Synced Synapse. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.xbmc.kore.ui; + +import android.annotation.TargetApi; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.astuetz.PagerSlidingTabStrip; + +import org.xbmc.kore.R; +import org.xbmc.kore.utils.LogUtils; +import org.xbmc.kore.utils.TabsAdapter; +import org.xbmc.kore.utils.UIUtils; +import org.xbmc.kore.utils.Utils; + +import butterknife.ButterKnife; +import butterknife.InjectView; + +/** + * Container for the TV Show overview and Episodes list + */ +public class AddonOverviewFragment extends Fragment { + private static final String TAG = LogUtils.makeLogTag(AddonOverviewFragment.class); + + private TabsAdapter tabsAdapter; + + @InjectView(R.id.pager_tab_strip) PagerSlidingTabStrip pagerTabStrip; + @InjectView(R.id.pager) ViewPager viewPager; + + /** + * Create a new instance of this, initialized to show the addon addonId + */ + @TargetApi(21) + public static AddonOverviewFragment newInstance(AddonListFragment.ViewHolder vh) { + AddonOverviewFragment fragment = new AddonOverviewFragment(); + + Bundle args = new Bundle(); + args.putString(AddonDetailsFragment.BUNDLE_KEY_ADDONID, vh.addonId); + args.putString(AddonDetailsFragment.BUNDLE_KEY_NAME, vh.addonName); + args.putString(AddonDetailsFragment.BUNDLE_KEY_AUTHOR, vh.author); + args.putString(AddonDetailsFragment.BUNDLE_KEY_VERSION, vh.version); + args.putString(AddonDetailsFragment.BUNDLE_KEY_SUMMARY, vh.summary); + args.putString(AddonDetailsFragment.BUNDLE_KEY_DESCRIPTION, vh.description); + args.putString(AddonDetailsFragment.BUNDLE_KEY_FANART, vh.fanart); + args.putString(AddonDetailsFragment.BUNDLE_KEY_POSTER, vh.poster); + args.putBoolean(AddonDetailsFragment.BUNDLE_KEY_ENABLED, vh.enabled); + + if( Utils.isLollipopOrLater()) { + args.putString(AddonDetailsFragment.POSTER_TRANS_NAME, vh.artView.getTransitionName()); + } + fragment.setArguments(args); + return fragment; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + Bundle args = getArguments(); + + if ((container == null) || (args == null)) { + // We're not being shown or there's nothing to show + return null; + } + + ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_default_view_pager, container, false); + ButterKnife.inject(this, root); + + Bundle plugin = new Bundle(); + MediaFileListFragment.FileLocation rootPath = new MediaFileListFragment.FileLocation( + args.getString(AddonDetailsFragment.BUNDLE_KEY_NAME, "Content"), + "plugin://" + args.getString(AddonDetailsFragment.BUNDLE_KEY_ADDONID, ""), + true); + rootPath.setRootDir(true); + plugin.putParcelable(MediaFileListFragment.ROOT_PATH, rootPath); + + long baseFragmentId = 1000; + tabsAdapter = new TabsAdapter(getActivity(), getChildFragmentManager()) + .addTab(AddonDetailsFragment.class, args, R.string.addon_overview, baseFragmentId++) + .addTab(MediaFileListFragment.class, plugin, R.string.addon_content, baseFragmentId++) + ; + viewPager.setAdapter(tabsAdapter); + pagerTabStrip.setViewPager(viewPager); + + return root; + } + + @Override + public void onActivityCreated (Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setHasOptionsMenu(false); + } + + public Fragment getCurrentTabFragment() { + return tabsAdapter.getItem(viewPager.getCurrentItem()); + } + + public View getSharedElement() { + View view = getView(); + if (view == null) + return null; + + //Note: this works as R.id.poster is only used in TVShowOverviewFragment. + //If the same id is used in other fragments in the TabsAdapter we + //need to check which fragment is currently displayed + View artView = view.findViewById(R.id.poster); + View scrollView = view.findViewById(R.id.media_panel); + if (( artView != null ) && + ( scrollView != null ) && + UIUtils.isViewInBounds(scrollView, artView)) { + return artView; + } + + return null; + } +} diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java b/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java index 5bd3fc6..1a02654 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java @@ -206,7 +206,7 @@ public class AddonsActivity extends BaseActivity selectedAddonTitle = vh.addonName; // Replace list fragment - final AddonDetailsFragment addonDetailsFragment = AddonDetailsFragment.newInstance(vh); + final AddonOverviewFragment addonDetailsFragment = AddonOverviewFragment.newInstance(vh); FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction(); // Set up transitions diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f97757f..48df2ad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -286,6 +286,9 @@ Overview Episodes + Overview + Content + Artists Albums Genres From b0f2adb876d6650b002bb97042491c227abfe53f Mon Sep 17 00:00:00 2001 From: David Eisner Date: Tue, 3 May 2016 20:39:53 +0100 Subject: [PATCH 03/10] Addon content in addons activity tabs --- .../xbmc/kore/ui/AddonOverviewFragment.java | 28 +++-- .../java/org/xbmc/kore/ui/AddonsActivity.java | 2 +- .../xbmc/kore/ui/AddonsOverviewFragment.java | 112 ++++++++++++++++++ .../java/org/xbmc/kore/utils/TabsAdapter.java | 17 ++- 4 files changed, 148 insertions(+), 11 deletions(-) create mode 100644 app/src/main/java/org/xbmc/kore/ui/AddonsOverviewFragment.java diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java b/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java index a8f3425..47a4166 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java @@ -16,6 +16,8 @@ package org.xbmc.kore.ui; import android.annotation.TargetApi; +import android.content.Context; +import android.content.SharedPreferences; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.view.ViewPager; @@ -31,6 +33,10 @@ import org.xbmc.kore.utils.TabsAdapter; import org.xbmc.kore.utils.UIUtils; import org.xbmc.kore.utils.Utils; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + import butterknife.ButterKnife; import butterknife.InjectView; @@ -70,6 +76,18 @@ public class AddonOverviewFragment extends Fragment { return fragment; } + public Bundle contentArgs(Bundle details) { + String name = details.getString(AddonDetailsFragment.BUNDLE_KEY_NAME, "Content"); + String path = details.getString(AddonDetailsFragment.BUNDLE_KEY_ADDONID); + + Bundle content = new Bundle(); + content.putString(AddonDetailsFragment.BUNDLE_KEY_NAME, name); + MediaFileListFragment.FileLocation rootPath = new MediaFileListFragment.FileLocation(name, "plugin://" + path, true); + rootPath.setRootDir(true); + content.putParcelable(MediaFileListFragment.ROOT_PATH, rootPath); + return content; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -87,18 +105,10 @@ public class AddonOverviewFragment extends Fragment { ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_default_view_pager, container, false); ButterKnife.inject(this, root); - Bundle plugin = new Bundle(); - MediaFileListFragment.FileLocation rootPath = new MediaFileListFragment.FileLocation( - args.getString(AddonDetailsFragment.BUNDLE_KEY_NAME, "Content"), - "plugin://" + args.getString(AddonDetailsFragment.BUNDLE_KEY_ADDONID, ""), - true); - rootPath.setRootDir(true); - plugin.putParcelable(MediaFileListFragment.ROOT_PATH, rootPath); - long baseFragmentId = 1000; tabsAdapter = new TabsAdapter(getActivity(), getChildFragmentManager()) .addTab(AddonDetailsFragment.class, args, R.string.addon_overview, baseFragmentId++) - .addTab(MediaFileListFragment.class, plugin, R.string.addon_content, baseFragmentId++) + .addTab(MediaFileListFragment.class, contentArgs(args), R.string.addon_content, baseFragmentId++) ; viewPager.setAdapter(tabsAdapter); pagerTabStrip.setViewPager(viewPager); diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java b/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java index 1a02654..7eff96a 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java @@ -70,7 +70,7 @@ public class AddonsActivity extends BaseActivity navigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout)); if (savedInstanceState == null) { - AddonListFragment addonListFragment = new AddonListFragment(); + AddonsOverviewFragment addonListFragment = new AddonsOverviewFragment(); // Setup animations if (Utils.isLollipopOrLater()) { diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonsOverviewFragment.java b/app/src/main/java/org/xbmc/kore/ui/AddonsOverviewFragment.java new file mode 100644 index 0000000..5752146 --- /dev/null +++ b/app/src/main/java/org/xbmc/kore/ui/AddonsOverviewFragment.java @@ -0,0 +1,112 @@ +/* + * Copyright 2016 Synced Synapse. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.xbmc.kore.ui; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.astuetz.PagerSlidingTabStrip; + +import org.xbmc.kore.R; +import org.xbmc.kore.utils.LogUtils; +import org.xbmc.kore.utils.TabsAdapter; +import org.xbmc.kore.utils.UIUtils; + +import java.util.Collections; +import java.util.Set; + +import butterknife.ButterKnife; +import butterknife.InjectView; + +/** + * Container for the TV Show overview and Episodes list + */ +public class AddonsOverviewFragment extends Fragment { + private static final String TAG = LogUtils.makeLogTag(AddonsOverviewFragment.class); + + private TabsAdapter tabsAdapter; + + @InjectView(R.id.pager_tab_strip) PagerSlidingTabStrip pagerTabStrip; + @InjectView(R.id.pager) ViewPager viewPager; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + if (container == null) { + // We're not being shown or there's nothing to show + return null; + } + + ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_default_view_pager, container, false); + ButterKnife.inject(this, root); + + tabsAdapter = new TabsAdapter(getActivity(), getChildFragmentManager()); + SharedPreferences prefs = getActivity().getSharedPreferences("addons", Context.MODE_PRIVATE); + Set bookmarked = prefs.getStringSet("bookmarked", Collections.emptySet()); + long baseFragmentId = 70 + bookmarked.size() * 100; + for (String path: bookmarked) { + String name = prefs.getString("name_" + path, "Content"); + Bundle addon = new Bundle(); + addon.putString(AddonDetailsFragment.BUNDLE_KEY_NAME, name); + addon.putParcelable(MediaFileListFragment.ROOT_PATH, new MediaFileListFragment.FileLocation(name, "plugin://" + path, true)); + tabsAdapter.addTab(MediaFileListFragment.class, addon, name, baseFragmentId++); + } + tabsAdapter.addTab(AddonListFragment.class, new Bundle(), R.string.addons, baseFragmentId); + viewPager.setAdapter(tabsAdapter); + pagerTabStrip.setViewPager(viewPager); + + return root; + } + + @Override + public void onActivityCreated (Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setHasOptionsMenu(false); + } + + public Fragment getCurrentTabFragment() { + return tabsAdapter.getItem(viewPager.getCurrentItem()); + } + + public View getSharedElement() { + View view = getView(); + if (view == null) + return null; + + //Note: this works as R.id.poster is only used in TVShowOverviewFragment. + //If the same id is used in other fragments in the TabsAdapter we + //need to check which fragment is currently displayed + View artView = view.findViewById(R.id.poster); + View scrollView = view.findViewById(R.id.media_panel); + if (( artView != null ) && + ( scrollView != null ) && + UIUtils.isViewInBounds(scrollView, artView)) { + return artView; + } + + return null; + } +} diff --git a/app/src/main/java/org/xbmc/kore/utils/TabsAdapter.java b/app/src/main/java/org/xbmc/kore/utils/TabsAdapter.java index ae845c5..7741376 100644 --- a/app/src/main/java/org/xbmc/kore/utils/TabsAdapter.java +++ b/app/src/main/java/org/xbmc/kore/utils/TabsAdapter.java @@ -38,12 +38,21 @@ public class TabsAdapter extends FragmentPagerAdapter { private final Bundle args; private final int titleRes; private final long fragmentId; + private final String titleString; TabInfo(Class fragmentClass, Bundle args, int titleRes, long fragmentId) { this.fragmentClass = fragmentClass; this.args = args; this.titleRes = titleRes; this.fragmentId = fragmentId; + this.titleString = null; + } + TabInfo(Class fragmentClass, Bundle args, String titleString, long fragmentId) { + this.fragmentClass = fragmentClass; + this.args = args; + this.titleRes = 0; + this.fragmentId = fragmentId; + this.titleString = titleString; } } @@ -59,6 +68,12 @@ public class TabsAdapter extends FragmentPagerAdapter { return this; } + public TabsAdapter addTab(Class fragmentClass, Bundle args, String titleString, long fragmentId) { + TabInfo info = new TabInfo(fragmentClass, args, titleString, fragmentId); + tabInfos.add(info); + return this; + } + @Override public int getCount() { return tabInfos.size(); @@ -101,7 +116,7 @@ public class TabsAdapter extends FragmentPagerAdapter { TabInfo tabInfo = tabInfos.get(position); if (tabInfo != null) { // return context.getString(tabInfo.titleRes).toUpperCase(Locale.getDefault()); - return context.getString(tabInfo.titleRes); + return tabInfo.titleString == null? context.getString(tabInfo.titleRes) : tabInfo.titleString; } return null; } From 51680a835c6a5203761c305b69c65234196c356f Mon Sep 17 00:00:00 2001 From: David Eisner Date: Wed, 4 May 2016 09:41:21 +0100 Subject: [PATCH 04/10] Pin an addon as an addons activity tab --- .../xbmc/kore/ui/AddonDetailsFragment.java | 51 +++++++++++++++++++ .../res/layout/fragment_addon_details.xml | 7 +++ app/src/main/res/values/strings.xml | 3 ++ 3 files changed, 61 insertions(+) diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonDetailsFragment.java b/app/src/main/java/org/xbmc/kore/ui/AddonDetailsFragment.java index d454c40..9aff48d 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonDetailsFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonDetailsFragment.java @@ -16,6 +16,8 @@ package org.xbmc.kore.ui; import android.annotation.TargetApi; +import android.content.Context; +import android.content.SharedPreferences; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Bundle; @@ -45,6 +47,10 @@ import org.xbmc.kore.utils.LogUtils; import org.xbmc.kore.utils.UIUtils; import org.xbmc.kore.utils.Utils; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + import butterknife.ButterKnife; import butterknife.InjectView; import butterknife.OnClick; @@ -80,6 +86,7 @@ public class AddonDetailsFragment extends Fragment { // Buttons @InjectView(R.id.fab) ImageButton fabButton; @InjectView(R.id.enable_disable) ImageButton enabledButton; + @InjectView(R.id.pin_unpin) ImageButton pinButton; // Detail views @InjectView(R.id.media_panel) ScrollView mediaPanel; @@ -170,6 +177,7 @@ public class AddonDetailsFragment extends Fragment { setImages(bundle.getString(BUNDLE_KEY_POSTER), bundle.getString(BUNDLE_KEY_FANART)); setupEnableButton(bundle.getBoolean(BUNDLE_KEY_ENABLED, false)); + updatePinButton(); // Pad main content view to overlap with bottom system bar // UIUtils.setPaddingForSystemBars(getActivity(), mediaPanel, false, false, true); @@ -318,4 +326,47 @@ public class AddonDetailsFragment extends Fragment { } }, callbackHandler); } + + @OnClick(R.id.pin_unpin) + public void onPinClicked(View v) { + final boolean enable = (v.getTag() == null)? true : !(Boolean)v.getTag(); + + String name = mediaTitle.getText().toString(); + String path = addonId; + + SharedPreferences prefs = getActivity().getSharedPreferences("addons", Context.MODE_PRIVATE); + Set bookmarked = new HashSet<>(prefs.getStringSet("bookmarked", Collections.emptySet())); + if (enable) + bookmarked.add(path); + else + bookmarked.remove(path); + prefs + .edit() + .putStringSet("bookmarked", bookmarked) + .putString("name_" + path, name) + .commit(); + Toast.makeText(getActivity(), enable? R.string.addon_pinned : R.string.addon_unpinned, Toast.LENGTH_SHORT).show(); + setupPinButton(enable); + } + + private void setupPinButton(boolean enabled) { + // Enabled button + if (enabled) { + Resources.Theme theme = getActivity().getTheme(); + TypedArray styledAttributes = theme.obtainStyledAttributes(new int[]{ + R.attr.colorAccent}); + pinButton.setColorFilter(styledAttributes.getColor(0, + getActivity().getResources().getColor(R.color.accent_default))); + styledAttributes.recycle(); + } else { + pinButton.clearColorFilter(); + } + pinButton.setTag(enabled); + } + + private void updatePinButton() { + SharedPreferences prefs = getActivity().getSharedPreferences("addons", Context.MODE_PRIVATE); + Set bookmarked = prefs.getStringSet("bookmarked", Collections.emptySet()); + setupPinButton(bookmarked.contains(addonId)); + } } diff --git a/app/src/main/res/layout/fragment_addon_details.xml b/app/src/main/res/layout/fragment_addon_details.xml index f675055..59e9473 100644 --- a/app/src/main/res/layout/fragment_addon_details.xml +++ b/app/src/main/res/layout/fragment_addon_details.xml @@ -97,6 +97,13 @@ android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent"/> + Enable/Disable Addon Addon enabled Addon disabled + Pin/Unpin Addon + Addon pinned + Addon unpinned Hide watched From 430de715d248cbab9615a1992e8e9e5c16856a53 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Wed, 4 May 2016 09:41:56 +0100 Subject: [PATCH 05/10] Avoid showing addon sources when touching the loading view --- app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java b/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java index 1df24a5..0a9585b 100644 --- a/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java @@ -143,7 +143,8 @@ public class MediaFileListFragment extends AbstractListFragment { emptyView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - browseSources(); + if (!atRootDirectory()) + browseSources(); } }); From 9bb35cb8cc892f290ad0c19a408118dbc8fb584d Mon Sep 17 00:00:00 2001 From: David Eisner Date: Fri, 6 May 2016 17:13:54 +0100 Subject: [PATCH 06/10] Addon fragment renamed to reduce confusion --- ...sOverviewFragment.java => AddonListContainerFragment.java} | 4 ++-- app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename app/src/main/java/org/xbmc/kore/ui/{AddonsOverviewFragment.java => AddonListContainerFragment.java} (96%) diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonsOverviewFragment.java b/app/src/main/java/org/xbmc/kore/ui/AddonListContainerFragment.java similarity index 96% rename from app/src/main/java/org/xbmc/kore/ui/AddonsOverviewFragment.java rename to app/src/main/java/org/xbmc/kore/ui/AddonListContainerFragment.java index 5752146..2ed0753 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonsOverviewFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonListContainerFragment.java @@ -40,8 +40,8 @@ import butterknife.InjectView; /** * Container for the TV Show overview and Episodes list */ -public class AddonsOverviewFragment extends Fragment { - private static final String TAG = LogUtils.makeLogTag(AddonsOverviewFragment.class); +public class AddonListContainerFragment extends Fragment { + private static final String TAG = LogUtils.makeLogTag(AddonListContainerFragment.class); private TabsAdapter tabsAdapter; diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java b/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java index 7eff96a..8b02c56 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java @@ -70,7 +70,7 @@ public class AddonsActivity extends BaseActivity navigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout)); if (savedInstanceState == null) { - AddonsOverviewFragment addonListFragment = new AddonsOverviewFragment(); + AddonListContainerFragment addonListFragment = new AddonListContainerFragment(); // Setup animations if (Utils.isLollipopOrLater()) { From 6eb1fcf167958af2b014d7ece15c5bdcd2cf4902 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Fri, 6 May 2016 18:50:31 +0100 Subject: [PATCH 07/10] Delay loading plugin content Some plugins trigger UI when you Kore attempts to browse. We don't want the Content tab to cause this when the user has only opened the Overview tab. For the tabs pinned to the addons view (AddonListContainerFragment), loading by the usual OffScreenPageLimit rules probably makes sense, as you can avoid pinning tabs that don't work well. --- .../org/xbmc/kore/ui/MediaFileListFragment.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java b/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java index 0a9585b..20d7803 100644 --- a/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java @@ -78,6 +78,7 @@ public class MediaFileListFragment extends AbstractListFragment { int playlistId = PlaylistType.MUSIC_PLAYLISTID; // this is the ID of the music player // private MediaFileListAdapter adapter = null; boolean browseRootAlready = false; + FileLocation loadOnVisible = null; ArrayList rootFileLocation = new ArrayList(); Queue mediaQueueFileLocation = new LinkedList<>(); @@ -162,8 +163,9 @@ public class MediaFileListFragment extends AbstractListFragment { ((MediaFileListAdapter) getAdapter()).setFilelistItems(list); } else if (rootPath != null) { - browseRootAlready = true; - browseDirectory(rootPath); + loadOnVisible = rootPath; + // setUserVisibleHint may have already fired + setUserVisibleHint(getUserVisibleHint()); } else { browseSources(); @@ -171,6 +173,17 @@ public class MediaFileListFragment extends AbstractListFragment { return root; } + @Override + public void setUserVisibleHint (boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if (isVisibleToUser && loadOnVisible != null) { + FileLocation rootPath = loadOnVisible; + loadOnVisible = null; + browseRootAlready = true; + browseDirectory(rootPath); + } + } + void handleFileSelect(FileLocation f) { // if selection is a directory, browse the the level below if (f.isDirectory) { From 57f3dfd50258c6c55f67b797037f9ea99ec6d5a5 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Fri, 6 May 2016 18:56:32 +0100 Subject: [PATCH 08/10] Limit delayed loading of plugin content to AddonOverviewFragment --- app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java | 1 + app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java b/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java index 47a4166..4ab8dd0 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java @@ -85,6 +85,7 @@ public class AddonOverviewFragment extends Fragment { MediaFileListFragment.FileLocation rootPath = new MediaFileListFragment.FileLocation(name, "plugin://" + path, true); rootPath.setRootDir(true); content.putParcelable(MediaFileListFragment.ROOT_PATH, rootPath); + content.putBoolean(MediaFileListFragment.DELAY_LOAD, true); return content; } diff --git a/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java b/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java index 20d7803..e180b0c 100644 --- a/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/MediaFileListFragment.java @@ -65,6 +65,7 @@ public class MediaFileListFragment extends AbstractListFragment { public static final String ROOT_PATH_CONTENTS = "rootPathContents"; public static final String ROOT_VISITED = "rootVisited"; public static final String ROOT_PATH = "rootPath"; + public static final String DELAY_LOAD = "delayLoad"; private static final String ADDON_SOURCE = "addons:"; private HostManager hostManager; @@ -165,7 +166,7 @@ public class MediaFileListFragment extends AbstractListFragment { else if (rootPath != null) { loadOnVisible = rootPath; // setUserVisibleHint may have already fired - setUserVisibleHint(getUserVisibleHint()); + setUserVisibleHint(getUserVisibleHint() || !args.getBoolean(DELAY_LOAD, false)); } else { browseSources(); From 52b5f45019dfd5f090d2063e63db2d9ad11aa6a2 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Fri, 6 May 2016 19:06:25 +0100 Subject: [PATCH 09/10] Keep the Addons tab on the left Following feedback from @SyncedSynapse --- .../java/org/xbmc/kore/ui/AddonListContainerFragment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonListContainerFragment.java b/app/src/main/java/org/xbmc/kore/ui/AddonListContainerFragment.java index 2ed0753..690bdb2 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonListContainerFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonListContainerFragment.java @@ -67,14 +67,14 @@ public class AddonListContainerFragment extends Fragment { SharedPreferences prefs = getActivity().getSharedPreferences("addons", Context.MODE_PRIVATE); Set bookmarked = prefs.getStringSet("bookmarked", Collections.emptySet()); long baseFragmentId = 70 + bookmarked.size() * 100; + tabsAdapter.addTab(AddonListFragment.class, new Bundle(), R.string.addons, baseFragmentId); for (String path: bookmarked) { String name = prefs.getString("name_" + path, "Content"); Bundle addon = new Bundle(); addon.putString(AddonDetailsFragment.BUNDLE_KEY_NAME, name); addon.putParcelable(MediaFileListFragment.ROOT_PATH, new MediaFileListFragment.FileLocation(name, "plugin://" + path, true)); - tabsAdapter.addTab(MediaFileListFragment.class, addon, name, baseFragmentId++); + tabsAdapter.addTab(MediaFileListFragment.class, addon, name, ++baseFragmentId); } - tabsAdapter.addTab(AddonListFragment.class, new Bundle(), R.string.addons, baseFragmentId); viewPager.setAdapter(tabsAdapter); pagerTabStrip.setViewPager(viewPager); From ba7931c5ea9cf992c4397b80495360df8f52a1f3 Mon Sep 17 00:00:00 2001 From: David Eisner Date: Fri, 6 May 2016 20:28:57 +0100 Subject: [PATCH 10/10] Only attempt to browse and pin pluginsource type plugins Using AddonOverviewFragment in both cases would probably be an easier to follow code path, but this implementation switches between using AddonDetailsFragment directly and adding it via AddonOverviewFragment. --- .../main/java/org/xbmc/kore/ui/AddonDetailsFragment.java | 9 +++++++-- .../main/java/org/xbmc/kore/ui/AddonListFragment.java | 2 ++ .../java/org/xbmc/kore/ui/AddonOverviewFragment.java | 4 +++- app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java | 6 +++++- .../java/org/xbmc/kore/ui/SharedElementFragment.java | 8 ++++++++ app/src/main/res/layout/fragment_addon_details.xml | 1 + 6 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 app/src/main/java/org/xbmc/kore/ui/SharedElementFragment.java diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonDetailsFragment.java b/app/src/main/java/org/xbmc/kore/ui/AddonDetailsFragment.java index 9aff48d..f16dc8b 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonDetailsFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonDetailsFragment.java @@ -58,7 +58,7 @@ import butterknife.OnClick; /** * Presents addon details */ -public class AddonDetailsFragment extends Fragment { +public class AddonDetailsFragment extends SharedElementFragment { private static final String TAG = LogUtils.makeLogTag(AddonDetailsFragment.class); public static final String BUNDLE_KEY_ADDONID = "addon_id"; @@ -71,6 +71,7 @@ public class AddonDetailsFragment extends Fragment { public static final String BUNDLE_KEY_FANART = "fanart"; public static final String BUNDLE_KEY_POSTER = "poster"; public static final String BUNDLE_KEY_ENABLED = "enabled"; + public static final String BUNDLE_KEY_BROWSABLE = "browsable"; private HostManager hostManager; private HostInfo hostInfo; @@ -119,6 +120,7 @@ public class AddonDetailsFragment extends Fragment { args.putString(BUNDLE_KEY_FANART, vh.fanart); args.putString(BUNDLE_KEY_POSTER, vh.poster); args.putBoolean(BUNDLE_KEY_ENABLED, vh.enabled); + args.putBoolean(BUNDLE_KEY_BROWSABLE, vh.browsable); if( Utils.isLollipopOrLater()) { args.putString(POSTER_TRANS_NAME, vh.artView.getTransitionName()); @@ -177,7 +179,8 @@ public class AddonDetailsFragment extends Fragment { setImages(bundle.getString(BUNDLE_KEY_POSTER), bundle.getString(BUNDLE_KEY_FANART)); setupEnableButton(bundle.getBoolean(BUNDLE_KEY_ENABLED, false)); - updatePinButton(); + if (bundle.getBoolean(BUNDLE_KEY_BROWSABLE, true)) + updatePinButton(); // Pad main content view to overlap with bottom system bar // UIUtils.setPaddingForSystemBars(getActivity(), mediaPanel, false, false, true); @@ -296,6 +299,7 @@ public class AddonDetailsFragment extends Fragment { * Returns the shared element if visible * @return View if visible, null otherwise */ + @Override public View getSharedElement() { if (UIUtils.isViewInBounds(mediaPanel, mediaPoster)) { return mediaPoster; @@ -362,6 +366,7 @@ public class AddonDetailsFragment extends Fragment { pinButton.clearColorFilter(); } pinButton.setTag(enabled); + pinButton.setVisibility(View.VISIBLE); } private void updatePinButton() { diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonListFragment.java b/app/src/main/java/org/xbmc/kore/ui/AddonListFragment.java index 1692fee..bfee6b8 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonListFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonListFragment.java @@ -265,6 +265,7 @@ public class AddonListFragment extends Fragment viewHolder.fanart = addonDetails.fanart; viewHolder.poster = addonDetails.thumbnail; viewHolder.enabled = addonDetails.enabled; + viewHolder.browsable = AddonType.Types.XBMC_PYTHON_PLUGINSOURCE.equals(addonDetails.type); viewHolder.titleView.setText(viewHolder.addonName); viewHolder.detailsView.setText(addonDetails.summary); @@ -297,5 +298,6 @@ public class AddonListFragment extends Fragment String fanart; String poster; Boolean enabled; + Boolean browsable; } } diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java b/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java index 4ab8dd0..b15c668 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonOverviewFragment.java @@ -43,7 +43,7 @@ import butterknife.InjectView; /** * Container for the TV Show overview and Episodes list */ -public class AddonOverviewFragment extends Fragment { +public class AddonOverviewFragment extends SharedElementFragment { private static final String TAG = LogUtils.makeLogTag(AddonOverviewFragment.class); private TabsAdapter tabsAdapter; @@ -68,6 +68,7 @@ public class AddonOverviewFragment extends Fragment { args.putString(AddonDetailsFragment.BUNDLE_KEY_FANART, vh.fanart); args.putString(AddonDetailsFragment.BUNDLE_KEY_POSTER, vh.poster); args.putBoolean(AddonDetailsFragment.BUNDLE_KEY_ENABLED, vh.enabled); + args.putBoolean(AddonDetailsFragment.BUNDLE_KEY_BROWSABLE, vh.browsable); if( Utils.isLollipopOrLater()) { args.putString(AddonDetailsFragment.POSTER_TRANS_NAME, vh.artView.getTransitionName()); @@ -127,6 +128,7 @@ public class AddonOverviewFragment extends Fragment { return tabsAdapter.getItem(viewPager.getCurrentItem()); } + @Override public View getSharedElement() { View view = getView(); if (view == null) diff --git a/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java b/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java index 8b02c56..18d3a88 100644 --- a/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java +++ b/app/src/main/java/org/xbmc/kore/ui/AddonsActivity.java @@ -206,7 +206,11 @@ public class AddonsActivity extends BaseActivity selectedAddonTitle = vh.addonName; // Replace list fragment - final AddonOverviewFragment addonDetailsFragment = AddonOverviewFragment.newInstance(vh); + final SharedElementFragment addonDetailsFragment = + vh.browsable + ? AddonOverviewFragment.newInstance(vh) + : AddonDetailsFragment.newInstance(vh) + ; FragmentTransaction fragTrans = getSupportFragmentManager().beginTransaction(); // Set up transitions diff --git a/app/src/main/java/org/xbmc/kore/ui/SharedElementFragment.java b/app/src/main/java/org/xbmc/kore/ui/SharedElementFragment.java new file mode 100644 index 0000000..19c56ce --- /dev/null +++ b/app/src/main/java/org/xbmc/kore/ui/SharedElementFragment.java @@ -0,0 +1,8 @@ +package org.xbmc.kore.ui; + +import android.support.v4.app.Fragment; +import android.view.View; + +public abstract class SharedElementFragment extends Fragment { + public abstract View getSharedElement(); +} diff --git a/app/src/main/res/layout/fragment_addon_details.xml b/app/src/main/res/layout/fragment_addon_details.xml index 59e9473..912ae69 100644 --- a/app/src/main/res/layout/fragment_addon_details.xml +++ b/app/src/main/res/layout/fragment_addon_details.xml @@ -99,6 +99,7 @@ android:layout_height="match_parent"/>