squashed merge of hacky-bottom branch
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_selected="true" android:color="@android:color/primary_text_dark" />
|
||||
<item android:color="@android:color/primary_text_dark" android:alpha="0.65" />
|
||||
<item android:state_selected="true" android:color="@android:color/background_light" />
|
||||
<item android:color="@android:color/background_light" android:alpha="0.65" />
|
||||
</selector>
|
||||
|
BIN
res/drawable-hdpi/ic_menu_moreoverflow_normal_holo_dark.png
Normal file
After Width: | Height: | Size: 144 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 396 B |
BIN
res/drawable-mdpi/ic_menu_moreoverflow_normal_holo_dark.png
Normal file
After Width: | Height: | Size: 122 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 247 B After Width: | Height: | Size: 247 B |
25
res/drawable-v21/ic_menu_moreoverflow.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<!--
|
||||
Copyright (C) 2014 The Android Open Source Project
|
||||
|
||||
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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2c-1.1,0 -2,0.9 -2,2S10.9,8 12,8zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2c1.1,0 2,-0.9 2,-2S13.1,10 12,10zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2c1.1,0 2,-0.9 2,-2S13.1,16 12,16z"
|
||||
android:fillColor="@android:color/white"/>
|
||||
</vector>
|
6
res/drawable-v21/ic_menu_search.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- holo style menu button drawable -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:drawable="@drawable/ic_menu_search_material" />
|
||||
</selector>
|
3
res/drawable-v21/unbound_ripple_light.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="#30ffffff" >
|
||||
</ripple>
|
BIN
res/drawable-xhdpi/ic_menu_moreoverflow_normal_holo_dark.png
Normal file
After Width: | Height: | Size: 167 B |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 465 B |
BIN
res/drawable-xxhdpi/ic_menu_moreoverflow_normal_holo_dark.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 728 B After Width: | Height: | Size: 728 B |
Before Width: | Height: | Size: 915 B After Width: | Height: | Size: 915 B |
6
res/drawable/ic_menu_moreoverflow.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- holo style menu button drawable -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:drawable="@drawable/ic_menu_moreoverflow_normal_holo_dark" />
|
||||
</selector>
|
6
res/drawable/ic_menu_search.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- holo style menu button drawable -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:drawable="@drawable/ic_menu_search_holo" />
|
||||
</selector>
|
6
res/drawable/unbound_ripple_light.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- holo style selector for pre-ripple devices -->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true"
|
||||
android:drawable="@android:color/holo_blue_dark" />
|
||||
</selector>
|
@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2012 Christopher Eby <kreed@kreed.org>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-->
|
||||
<ch.blinkenlights.android.vanilla.ActionBarControls
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent"
|
||||
android:clickable="true">
|
||||
<ImageView
|
||||
android:id="@+id/cover"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:layout_width="36dip"
|
||||
android:layout_height="36dip"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/cover_art" />
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:textSize="14sp"
|
||||
android:singleLine="true"
|
||||
android:gravity="center_vertical"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="fill_parent"
|
||||
android:textColor="@android:color/primary_text_dark"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:id="@+id/artist"
|
||||
android:textSize="12sp"
|
||||
android:singleLine="true"
|
||||
android:gravity="center_vertical"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="fill_parent"
|
||||
android:textColor="@android:color/secondary_text_dark"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
</ch.blinkenlights.android.vanilla.ActionBarControls>
|
85
res/layout/bottombar_controls.xml
Normal file
@ -0,0 +1,85 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2016 Adrian Ulrich <adrian@blinkenlights.ch>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<ch.blinkenlights.android.vanilla.BottomBarControls
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:background="@color/tabs_background"
|
||||
android:elevation="8dp"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_height="@dimen/row_normal_height"
|
||||
android:layout_width="fill_parent"
|
||||
android:background="@drawable/unbound_ripple_light"
|
||||
android:gravity="center_vertical"
|
||||
android:id="@+id/content_controls">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/cover"
|
||||
android:scaleType="centerCrop"
|
||||
android:layout_width="@dimen/cover_size"
|
||||
android:layout_height="@dimen/cover_size"
|
||||
android:layout_marginLeft="@dimen/cover_padding"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:contentDescription="@string/cover_art" />
|
||||
<LinearLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="0px"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginLeft="@dimen/text_padding"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:textSize="14sp"
|
||||
android:singleLine="true"
|
||||
android:gravity="center_vertical"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="fill_parent"
|
||||
android:textColor="@android:color/background_light"
|
||||
android:layout_height="wrap_content" />
|
||||
<TextView
|
||||
android:id="@+id/artist"
|
||||
android:textSize="12sp"
|
||||
android:singleLine="true"
|
||||
android:gravity="center_vertical"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_width="fill_parent"
|
||||
android:textColor="@android:color/secondary_text_dark"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<SearchView
|
||||
android:visibility="gone"
|
||||
android:layout_height="@dimen/row_normal_height"
|
||||
android:layout_width="fill_parent"
|
||||
android:iconifiedByDefault="false"
|
||||
android:imeOptions="actionSearch|flagNoFullscreen"
|
||||
android:id="@+id/search_view" />
|
||||
|
||||
|
||||
</ch.blinkenlights.android.vanilla.BottomBarControls>
|
@ -31,6 +31,7 @@ THE SOFTWARE.
|
||||
android:id="@+id/sliding_tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="4dp"
|
||||
android:background="@color/tabs_background" />
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
@ -49,6 +50,7 @@ THE SOFTWARE.
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="fill_parent" />
|
||||
</HorizontalScrollView>
|
||||
<include layout="@layout/bottombar_controls" android:id="@+id/bottombar_controls" />
|
||||
<include layout="@layout/permission_request" />
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
<!-- library tabs -->
|
||||
<color name="tabs_background">@color/vanillaPrimary</color>
|
||||
<color name="tabs_active_indicator">@android:color/primary_text_dark</color>
|
||||
<color name="tabs_active_indicator">@android:color/background_light</color>
|
||||
|
||||
<!-- themed overlay colors for full playback activity -->
|
||||
<color name="overlay_background_light">#ffeeeeee</color>
|
||||
|
@ -39,7 +39,8 @@ Copyright (C) 2015 Adrian Ulrich <adrian@blinkenlights.ch>
|
||||
</style>
|
||||
|
||||
<style name="Library" parent="VanillaBase">
|
||||
<item name="android:actionBarStyle">@style/Universal.LibraryActionBar</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<!-- universal styles -->
|
||||
@ -48,11 +49,6 @@ Copyright (C) 2015 Adrian Ulrich <adrian@blinkenlights.ch>
|
||||
<item name="android:background">@color/vanillaPrimary</item>
|
||||
</style>
|
||||
|
||||
<style name="Universal.LibraryActionBar" parent="android:Widget.Material.ActionBar">
|
||||
<item name="android:displayOptions"></item>
|
||||
<item name="android:background">@color/vanillaPrimary</item>
|
||||
</style>
|
||||
|
||||
|
||||
<!-- dark theme -->
|
||||
<style name="Dark.VanillaBase" parent="android:Theme.Material">
|
||||
@ -73,7 +69,8 @@ Copyright (C) 2015 Adrian Ulrich <adrian@blinkenlights.ch>
|
||||
</style>
|
||||
|
||||
<style name="Dark.Library" parent="Dark.VanillaBase">
|
||||
<item name="android:actionBarStyle">@style/Universal.LibraryActionBar</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
|
||||
|
@ -46,10 +46,8 @@ THE SOFTWARE.
|
||||
<item name="android:displayOptions">showTitle|homeAsUp</item>
|
||||
</style>
|
||||
<style name="Library" parent="VanillaBase">
|
||||
<item name="android:actionBarStyle">@style/LibraryActionBar</item>
|
||||
</style>
|
||||
<style name="LibraryActionBar" parent="android:Widget.Holo.ActionBar">
|
||||
<item name="android:displayOptions"></item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -23,8 +23,6 @@ import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
/**
|
||||
* Simple wrapper for SlidingTabLayout which takes
|
||||
* care of setting sane per-platform defaults
|
||||
@ -52,26 +50,11 @@ public class VanillaTabLayout extends SlidingTabLayout {
|
||||
protected TextView createDefaultTabView(Context context) {
|
||||
TextView view = super.createDefaultTabView(context);
|
||||
view.setTextColor(getResources().getColorStateList(ch.blinkenlights.android.vanilla.R.color.tab_text_selector));
|
||||
view.setBackgroundResource(ch.blinkenlights.android.vanilla.R.drawable.unbound_ripple_light);
|
||||
view.setMaxLines(1);
|
||||
view.setEllipsize(TextUtils.TruncateAt.END);
|
||||
view.setTextSize(14);
|
||||
return view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Borrow elevation of given action bar
|
||||
*
|
||||
* @param ab The active action bar
|
||||
*/
|
||||
public void inheritElevation(ActionBar ab) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
return; // noop on earlier releases
|
||||
|
||||
float elevation = ab.getElevation();
|
||||
ab.setElevation(0.0f);
|
||||
setElevation(elevation);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Adrian Ulrich <adrian@blinkenlights.ch>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package ch.blinkenlights.android.vanilla;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.LinearLayout;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
/**
|
||||
* LinearLayout that contains some hacks for sizing inside an ActionBar.
|
||||
*/
|
||||
public class ActionBarControls extends LinearLayout {
|
||||
|
||||
private final int dpiElementLp = 52; // Size of the ActionBarSearch icon in 5.x (50 + some slack)
|
||||
private final int dpiElementHolo = 64; // Size of the ActionBarSearch icon in HOLO
|
||||
private final int dpiMaxWidth = 350; // Never use more then 350 DPIs
|
||||
private final int visibleElements = 2; // The ActionBarSearch + Menu icons are visible
|
||||
|
||||
public ActionBarControls(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void onMeasure(int ws, int hs) {
|
||||
super.onMeasure(ws, hs);
|
||||
|
||||
final float density = getResources().getDisplayMetrics().density;
|
||||
final int dpiElement = ( android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? dpiElementLp : dpiElementHolo );
|
||||
int widthMode = MeasureSpec.getMode(ws);
|
||||
|
||||
if (widthMode != MeasureSpec.EXACTLY) {
|
||||
float dpiAvailable = (getSmallestAxisPx() / density) - (dpiElement * visibleElements);
|
||||
if (dpiAvailable > dpiMaxWidth || dpiAvailable < 1) {
|
||||
dpiAvailable = dpiMaxWidth;
|
||||
}
|
||||
setMeasuredDimension((int)(dpiAvailable * density), (int)(dpiElement * density));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the smaller axis of the display dimensions
|
||||
* @return The dimension of the smaller axis in pixels
|
||||
*/
|
||||
private final int getSmallestAxisPx() {
|
||||
DisplayMetrics metrics = getResources().getDisplayMetrics();
|
||||
return (metrics.widthPixels > metrics.heightPixels ? metrics.heightPixels : metrics.widthPixels);
|
||||
}
|
||||
|
||||
}
|
283
src/ch/blinkenlights/android/vanilla/BottomBarControls.java
Normal file
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Adrian Ulrich <adrian@blinkenlights.ch>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package ch.blinkenlights.android.vanilla;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.SearchView;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
||||
public class BottomBarControls extends LinearLayout
|
||||
implements View.OnClickListener
|
||||
, PopupMenu.OnMenuItemClickListener
|
||||
{
|
||||
/**
|
||||
* The application context
|
||||
*/
|
||||
private final Context mContext;
|
||||
/**
|
||||
* The title of the currently playing song
|
||||
*/
|
||||
private TextView mTitle;
|
||||
/**
|
||||
* The artist of the currently playing song
|
||||
*/
|
||||
private TextView mArtist;
|
||||
/**
|
||||
* Cover image
|
||||
*/
|
||||
private ImageView mCover;
|
||||
/**
|
||||
* A layout hosting the song information
|
||||
*/
|
||||
private LinearLayout mControlsContent;
|
||||
/**
|
||||
* Standard android search view
|
||||
*/
|
||||
private SearchView mSearchView;
|
||||
/**
|
||||
* ControlsContent click consumer, may be null
|
||||
*/
|
||||
private View.OnClickListener mParentClickConsumer;
|
||||
/**
|
||||
* Owner of our options menu and consumer of clicks
|
||||
*/
|
||||
private Activity mParentMenuConsumer;
|
||||
|
||||
|
||||
public BottomBarControls(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
mTitle = (TextView)findViewById(R.id.title);
|
||||
mArtist = (TextView)findViewById(R.id.artist);
|
||||
mCover = (ImageView)findViewById(R.id.cover);
|
||||
mSearchView = (SearchView)findViewById(R.id.search_view);
|
||||
mControlsContent = (LinearLayout)findViewById(R.id.content_controls);
|
||||
|
||||
styleSearchView(mSearchView, mContext.getResources().getColor(android.R.color.background_light));
|
||||
|
||||
super.onFinishInflate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Object tag = view.getTag();
|
||||
if (tag instanceof PopupMenu) {
|
||||
((PopupMenu)tag).show();
|
||||
} else if (tag instanceof MenuItem) {
|
||||
mParentMenuConsumer.onOptionsItemSelected((MenuItem)tag);
|
||||
} else if (view == mControlsContent && mParentClickConsumer != null) {
|
||||
// dispatch this click to parent, claiming it came from
|
||||
// the top view (= this)
|
||||
mParentClickConsumer.onClick(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
return mParentMenuConsumer.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
// Forcefully hide (and clear) search as we are not going to restore the state
|
||||
showSearch(false);
|
||||
return super.onSaveInstanceState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ControlsContent to be clickable
|
||||
*/
|
||||
public void setOnClickListener(View.OnClickListener listener) {
|
||||
mParentClickConsumer = listener;
|
||||
mControlsContent.setOnClickListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a query text listener for the search view
|
||||
*/
|
||||
public void setOnQueryTextListener(SearchView.OnQueryTextListener owner) {
|
||||
mSearchView.setOnQueryTextListener(owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots the options menu
|
||||
*
|
||||
* @param owner the activity who will receive our callbacks
|
||||
*/
|
||||
public void enableOptionsMenu(Activity owner) {
|
||||
mParentMenuConsumer = owner;
|
||||
|
||||
ImageButton menuButton = getImageButton(getResources().getDrawable(R.drawable.ic_menu_moreoverflow));
|
||||
PopupMenu popupMenu = (menuMargin() ? new PopupMenu(mContext, menuButton, Gravity.RIGHT) : new PopupMenu(mContext, menuButton));
|
||||
popupMenu.setOnMenuItemClickListener(this);
|
||||
|
||||
// Let parent populate the menu
|
||||
mParentMenuConsumer.onCreateOptionsMenu(popupMenu.getMenu());
|
||||
|
||||
// The menu is now ready, we an now add all invisible
|
||||
// items to the toolbar
|
||||
Menu menu = popupMenu.getMenu();
|
||||
for (int i=0; i < menu.size(); i++) {
|
||||
MenuItem menuItem = menu.getItem(i);
|
||||
if (menuItem.isVisible() == false) {
|
||||
ImageButton button = getImageButton(menuItem.getIcon());
|
||||
button.setTag(menuItem);
|
||||
button.setOnClickListener(this);
|
||||
mControlsContent.addView(button, -1);
|
||||
}
|
||||
}
|
||||
|
||||
// Add menu button at end of view
|
||||
menuButton.setTag(popupMenu);
|
||||
menuButton.setOnClickListener(this);
|
||||
mControlsContent.addView(menuButton, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the OptionsMenu of this view
|
||||
*/
|
||||
public void openMenu() {
|
||||
// simulates a click on the rightmost child which should be the options menu
|
||||
mControlsContent.getChildAt(mControlsContent.getChildCount()-1).performClick();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the search view to given state
|
||||
*
|
||||
* @param visible enables or disables the search box visibility
|
||||
* @return boolean old state
|
||||
*/
|
||||
public boolean showSearch(boolean visible) {
|
||||
boolean wasVisible = mSearchView.getVisibility() == View.VISIBLE;
|
||||
if (wasVisible != visible) {
|
||||
mSearchView.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
mControlsContent.setVisibility(visible ? View.GONE : View.VISIBLE);
|
||||
if (visible)
|
||||
mSearchView.setIconified(false); // requests focus AND shows the soft keyboard even if the view already was expanded
|
||||
else
|
||||
mSearchView.setQuery("", false);
|
||||
}
|
||||
return wasVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the cover image of this view
|
||||
*
|
||||
* @param cover the bitmap to display. Will use a placeholder image if cover is null
|
||||
*/
|
||||
public void setCover(Bitmap cover) {
|
||||
if (cover == null)
|
||||
mCover.setImageResource(R.drawable.fallback_cover);
|
||||
else
|
||||
mCover.setImageBitmap(cover);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the song metadata
|
||||
*
|
||||
* @param song the song info to display, may be null
|
||||
*/
|
||||
public void setSong(Song song) {
|
||||
if (song == null) {
|
||||
mTitle.setText(null);
|
||||
mArtist.setText(null);
|
||||
mCover.setImageBitmap(null);
|
||||
} else {
|
||||
Resources res = mContext.getResources();
|
||||
String title = song.title == null ? res.getString(R.string.unknown) : song.title;
|
||||
String artist = song.artist == null ? res.getString(R.string.unknown) : song.artist;
|
||||
mTitle.setText(title);
|
||||
mArtist.setText(artist);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new image button to be placed on the bar
|
||||
*
|
||||
* @param drawable The icon to use
|
||||
*/
|
||||
private ImageButton getImageButton(Drawable drawable) {
|
||||
|
||||
ImageButton button = new ImageButton(mContext);
|
||||
button.setImageDrawable(drawable);
|
||||
button.setBackgroundResource(R.drawable.unbound_ripple_light);
|
||||
|
||||
if (menuMargin()) {
|
||||
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
params.rightMargin = (int)(getResources().getDisplayMetrics().density * 4.0f);
|
||||
button.setLayoutParams(params);
|
||||
}
|
||||
return button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changing the colors of a search view is a MAJOR pain using XML
|
||||
* This cheap trick just loop trough the view and changes the
|
||||
* color of all text- and image views to 'style'
|
||||
*
|
||||
* @param view the view to search
|
||||
* @param color the color to apply
|
||||
*/
|
||||
private void styleSearchView(View view, int color) {
|
||||
if (view != null) {
|
||||
if (view instanceof TextView) {
|
||||
((TextView)view).setTextColor(color);
|
||||
} else if (view instanceof ImageView) {
|
||||
((ImageView)view).setColorFilter(color);
|
||||
} else if (view instanceof ViewGroup) {
|
||||
ViewGroup group = (ViewGroup)view;
|
||||
for (int i=0; i< group.getChildCount(); i++) {
|
||||
styleSearchView(group.getChildAt(i), color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we need to add a margin to the menu.
|
||||
* Because ...reasons.
|
||||
*/
|
||||
private boolean menuMargin() {
|
||||
return ThemeHelper.usesHoloTheme() == false;
|
||||
}
|
||||
}
|
@ -129,12 +129,8 @@ public class LibraryActivity
|
||||
|
||||
public ViewPager mViewPager;
|
||||
|
||||
private View mActionControls;
|
||||
private TextView mTitle;
|
||||
private TextView mArtist;
|
||||
private ImageView mCover;
|
||||
private BottomBarControls mBottomBarControls;
|
||||
private View mPermissionRequest;
|
||||
private MenuItem mSearchMenuItem;
|
||||
|
||||
private HorizontalScrollView mLimiterScroller;
|
||||
private ViewGroup mLimiterViews;
|
||||
@ -186,12 +182,10 @@ public class LibraryActivity
|
||||
|
||||
SharedPreferences settings = PlaybackService.getSettings(this);
|
||||
|
||||
View controls = getLayoutInflater().inflate(R.layout.actionbar_controls, null);
|
||||
mTitle = (TextView)controls.findViewById(R.id.title);
|
||||
mArtist = (TextView)controls.findViewById(R.id.artist);
|
||||
mCover = (ImageView)controls.findViewById(R.id.cover);
|
||||
controls.setOnClickListener(this);
|
||||
mActionControls = controls;
|
||||
mBottomBarControls = (BottomBarControls)findViewById(R.id.bottombar_controls);
|
||||
mBottomBarControls.setOnClickListener(this);
|
||||
mBottomBarControls.setOnQueryTextListener(this);
|
||||
mBottomBarControls.enableOptionsMenu(this);
|
||||
|
||||
mPermissionRequest = (View)findViewById(R.id.permission_request);
|
||||
|
||||
@ -202,7 +196,6 @@ public class LibraryActivity
|
||||
}
|
||||
|
||||
mVanillaTabLayout = (VanillaTabLayout)findViewById(R.id.sliding_tabs);
|
||||
mVanillaTabLayout.inheritElevation(getActionBar());
|
||||
mVanillaTabLayout.setOnPageChangeListener(pagerAdapter);
|
||||
|
||||
loadTabOrder();
|
||||
@ -291,17 +284,9 @@ public class LibraryActivity
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
Limiter limiter = mPagerAdapter.getCurrentLimiter();
|
||||
MenuItem menu_item = mSearchMenuItem;
|
||||
|
||||
if (menu_item != null) {
|
||||
// Check if we can collapse the search view
|
||||
// if we can, then it was open and we handled this
|
||||
// action
|
||||
boolean did_collapse = menu_item.collapseActionView();
|
||||
if (did_collapse == true) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mBottomBarControls.showSearch(false))
|
||||
break;
|
||||
|
||||
if (limiter != null) {
|
||||
int pos = -1;
|
||||
@ -336,6 +321,10 @@ public class LibraryActivity
|
||||
finish();
|
||||
}
|
||||
break;
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
// We intercept these to avoid showing the activity-default menu
|
||||
mBottomBarControls.openMenu();
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -522,10 +511,7 @@ public class LibraryActivity
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (cover == null)
|
||||
mCover.setImageResource(R.drawable.fallback_cover);
|
||||
else
|
||||
mCover.setImageBitmap(cover);
|
||||
mBottomBarControls.setCover(cover);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -533,7 +519,7 @@ public class LibraryActivity
|
||||
@Override
|
||||
public void onClick(View view)
|
||||
{
|
||||
if (view == mCover || view == mActionControls) {
|
||||
if (view == mBottomBarControls) {
|
||||
openPlaybackActivity();
|
||||
} else if (view == mPermissionRequest) {
|
||||
PermissionRequestActivity.requestPermissions(this, getIntent());
|
||||
@ -811,25 +797,12 @@ public class LibraryActivity
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu)
|
||||
{
|
||||
MenuItem controls = menu.add(0, MENU_PLAYBACK, 0, R.string.playback_view);
|
||||
controls.setActionView(mActionControls);
|
||||
controls.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
|
||||
// Call super after adding the now-playing view as this should be the first item
|
||||
// called before super to have it on top
|
||||
menu.add(0, MENU_PLAYBACK, 0, R.string.playback_view);
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
||||
// Check if we're running on Android 5.0 or higher
|
||||
if (ThemeHelper.usesHoloTheme()) {
|
||||
// Keep using the old icon
|
||||
mSearchMenuItem = menu.add(0, MENU_SEARCH, 0, R.string.search).setIcon(R.drawable.ic_menu_search);
|
||||
} else {
|
||||
// Use the new material search icon
|
||||
mSearchMenuItem = menu.add(0, MENU_SEARCH, 0, R.string.search).setIcon(R.drawable.ic_action_search);
|
||||
}
|
||||
mSearchMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | MenuItem.SHOW_AS_ACTION_ALWAYS);
|
||||
SearchView mSearchView = new SearchView(getActionBar().getThemedContext());
|
||||
mSearchView.setOnQueryTextListener(this);
|
||||
mSearchMenuItem.setActionView(mSearchView);
|
||||
MenuItem search = menu.add(0, MENU_SEARCH, 0, R.string.search).setIcon(R.drawable.ic_menu_search);
|
||||
search.setVisible(false);
|
||||
|
||||
menu.add(0, MENU_SORT, 0, R.string.sort_by).setIcon(R.drawable.ic_menu_sort_alphabetically);
|
||||
menu.add(0, MENU_SHOW_QUEUE, 0, R.string.show_queue);
|
||||
@ -850,7 +823,7 @@ public class LibraryActivity
|
||||
{
|
||||
switch (item.getItemId()) {
|
||||
case MENU_SEARCH:
|
||||
// this does nothing: expanding ishandled by mSearchView
|
||||
mBottomBarControls.showSearch(true);
|
||||
return true;
|
||||
case MENU_PLAYBACK:
|
||||
openPlaybackActivity();
|
||||
@ -963,22 +936,9 @@ public class LibraryActivity
|
||||
{
|
||||
super.onSongChange(song);
|
||||
|
||||
if (mTitle != null) {
|
||||
if (song == null) {
|
||||
mTitle.setText(null);
|
||||
mArtist.setText(null);
|
||||
mCover.setImageBitmap(null);
|
||||
} else {
|
||||
Resources res = getResources();
|
||||
String title = song.title == null ? res.getString(R.string.unknown) : song.title;
|
||||
String artist = song.artist == null ? res.getString(R.string.unknown) : song.artist;
|
||||
mTitle.setText(title);
|
||||
mArtist.setText(artist);
|
||||
// Update and generate the cover in a background thread
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_COVER, song));
|
||||
}
|
||||
mCover.setVisibility(CoverCache.mCoverLoadMode == 0 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
mBottomBarControls.setSong(song);
|
||||
if (song != null)
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_COVER, song));
|
||||
}
|
||||
|
||||
@Override
|
||||
|