diff options
76 files changed, 2212 insertions, 524 deletions
diff --git a/apps/Development/res/layout/connectivity.xml b/apps/Development/res/layout/connectivity.xml index 2aaf6c61f..d23a6b1b9 100644 --- a/apps/Development/res/layout/connectivity.xml +++ b/apps/Development/res/layout/connectivity.xml @@ -274,6 +274,16 @@ android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> + <Button android:id="@+id/report_all_bad" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/report_all_bad" /> + </LinearLayout> + + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content"> <Button android:id="@+id/crash" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/apps/Development/res/values/strings.xml b/apps/Development/res/values/strings.xml index 0c13987c4..d670cf4c4 100644 --- a/apps/Development/res/values/strings.xml +++ b/apps/Development/res/values/strings.xml @@ -43,6 +43,8 @@ <string name="start_hipri">Start HiPri</string> <string name="stop_hipri">Stop HiPri</string> <string name="crash">CRASH</string> + <string name="report_all_bad">Report all bad</string> + <string name="netid">NetId</string> <string name="add_default_route">Add Default Route</string> <string name="remove_default_route">Remove Default Route</string> diff --git a/apps/Development/src/com/android/development/Connectivity.java b/apps/Development/src/com/android/development/Connectivity.java index 93d199b15..a576eb397 100644 --- a/apps/Development/src/com/android/development/Connectivity.java +++ b/apps/Development/src/com/android/development/Connectivity.java @@ -29,6 +29,7 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; import android.net.ConnectivityManager; import android.net.LinkAddress; +import android.net.Network; import android.net.NetworkUtils; import android.net.RouteInfo; import android.net.wifi.ScanResult; @@ -304,6 +305,7 @@ public class Connectivity extends Activity { findViewById(R.id.stop_mms).setOnClickListener(mClickListener); findViewById(R.id.start_hipri).setOnClickListener(mClickListener); findViewById(R.id.stop_hipri).setOnClickListener(mClickListener); + findViewById(R.id.report_all_bad).setOnClickListener(mClickListener); findViewById(R.id.crash).setOnClickListener(mClickListener); findViewById(R.id.add_default_route).setOnClickListener(mClickListener); @@ -318,6 +320,11 @@ public class Connectivity extends Activity { registerReceiver(mReceiver, new IntentFilter(CONNECTIVITY_TEST_ALARM)); } + @Override + public void onDestroy() { + super.onDestroy(); + unregisterReceiver(mReceiver); + } @Override public void onResume() { @@ -387,6 +394,9 @@ public class Connectivity extends Activity { case R.id.add_default_route: onAddDefaultRoute(); break; + case R.id.report_all_bad: + onReportAllBad(); + break; case R.id.crash: onCrash(); break; @@ -460,6 +470,13 @@ public class Connectivity extends Activity { private void onStopScreenCycle() { } + private void onReportAllBad() { + Network[] networks = mCm.getAllNetworks(); + for (Network network : networks) { + mCm.reportBadNetwork(network); + } + } + private void onCrash() { ConnectivityManager foo = null; foo.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, diff --git a/build/product_sdk.mk b/build/product_sdk.mk index 7aba4ee67..68a4ecc60 100644 --- a/build/product_sdk.mk +++ b/build/product_sdk.mk @@ -44,4 +44,5 @@ PRODUCT_PACKAGES += \ layoutlib-tests \ llvm-rs-cc \ sqlite3 \ - vgabios-cirrus.bin + vgabios-cirrus.bin \ + split-select diff --git a/build/sdk-windows-x86.atree b/build/sdk-windows-x86.atree index a0e19ba22..7ff3add13 100644 --- a/build/sdk-windows-x86.atree +++ b/build/sdk-windows-x86.atree @@ -61,6 +61,9 @@ bin/etc1tool.exe strip platform-tools/etc1tool.exe rm build-tools/${PLATFORM_NAME}/aapt bin/aapt.exe strip build-tools/${PLATFORM_NAME}/aapt.exe +rm build-tools/${PLATFORM_NAME}/split-select +bin/split-select.exe strip build-tools/${PLATFORM_NAME}/split-select.exe + rm build-tools/${PLATFORM_NAME}/aidl bin/aidl.exe strip build-tools/${PLATFORM_NAME}/aidl.exe diff --git a/build/sdk.atree b/build/sdk.atree index 229e782f0..9c7ba8a9c 100644 --- a/build/sdk.atree +++ b/build/sdk.atree @@ -79,6 +79,7 @@ development/sdk/build_tools_runtime.properties build-tools/${PLATFORM_NAME}/runt # build tools from out/host/$(HOST_OS)-$(HOST_ARCH)/ bin/aapt strip build-tools/${PLATFORM_NAME}/aapt bin/aidl strip build-tools/${PLATFORM_NAME}/aidl +bin/split-select strip build-tools/${PLATFORM_NAME}/split-select bin/zipalign strip build-tools/${PLATFORM_NAME}/zipalign # renderscript (cc + headers) diff --git a/build/tools/windows_sdk.mk b/build/tools/windows_sdk.mk index d107d3712..dadb77313 100644 --- a/build/tools/windows_sdk.mk +++ b/build/tools/windows_sdk.mk @@ -41,6 +41,7 @@ WIN_TARGETS := \ prebuilt \ sqlite3 \ zipalign \ + split-select \ $(WIN_SDK_TARGETS) # This is the list of *Linux* build tools that we need diff --git a/build/windows_sdk_whitelist.mk b/build/windows_sdk_whitelist.mk index dfeaf3a55..d91dde5d1 100644 --- a/build/windows_sdk_whitelist.mk +++ b/build/windows_sdk_whitelist.mk @@ -40,6 +40,7 @@ subdirs += \ external/clang \ external/easymock \ external/expat \ + external/gtest \ external/libcxx \ external/libcxxabi \ external/compiler-rt \ diff --git a/docs/copyright-templates/asm.txt b/docs/copyright-templates/asm.txt index 22f9dd20d..4e79d32c5 100644 --- a/docs/copyright-templates/asm.txt +++ b/docs/copyright-templates/asm.txt @@ -1,4 +1,4 @@ -; Copyright 2013 The Android Open Source Project +; Copyright 2015 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. diff --git a/docs/copyright-templates/bash.txt b/docs/copyright-templates/bash.txt index 389b21a08..65cc335ad 100644 --- a/docs/copyright-templates/bash.txt +++ b/docs/copyright-templates/bash.txt @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright 2013 The Android Open Source Project +# Copyright 2015 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. diff --git a/docs/copyright-templates/bsd/c.txt b/docs/copyright-templates/bsd/c.txt index 55932e72f..1fe17d597 100644 --- a/docs/copyright-templates/bsd/c.txt +++ b/docs/copyright-templates/bsd/c.txt @@ -1,5 +1,5 @@ /* - * Copyright 2013 The Android Open Source Project + * Copyright 2015 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/docs/copyright-templates/c.txt b/docs/copyright-templates/c.txt index 11644d2ba..a1a67bfa6 100644 --- a/docs/copyright-templates/c.txt +++ b/docs/copyright-templates/c.txt @@ -1,5 +1,5 @@ /* - * Copyright 2013 The Android Open Source Project + * Copyright 2015 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. diff --git a/docs/copyright-templates/java.txt b/docs/copyright-templates/java.txt index 11644d2ba..a1a67bfa6 100644 --- a/docs/copyright-templates/java.txt +++ b/docs/copyright-templates/java.txt @@ -1,5 +1,5 @@ /* - * Copyright 2013 The Android Open Source Project + * Copyright 2015 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. diff --git a/docs/copyright-templates/make.txt b/docs/copyright-templates/make.txt index 317c4e698..298771095 100644 --- a/docs/copyright-templates/make.txt +++ b/docs/copyright-templates/make.txt @@ -1,4 +1,4 @@ -# Copyright 2013 The Android Open Source Project +# Copyright 2015 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. diff --git a/docs/copyright-templates/plain.txt b/docs/copyright-templates/plain.txt index 5db92824a..8545607fc 100644 --- a/docs/copyright-templates/plain.txt +++ b/docs/copyright-templates/plain.txt @@ -1,4 +1,4 @@ -Copyright 2013 The Android Open Source Project +Copyright 2015 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. diff --git a/docs/copyright-templates/sh.txt b/docs/copyright-templates/sh.txt index 7bf532bbd..254134ae4 100644 --- a/docs/copyright-templates/sh.txt +++ b/docs/copyright-templates/sh.txt @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2013 The Android Open Source Project +# Copyright 2015 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. diff --git a/docs/copyright-templates/xml.txt b/docs/copyright-templates/xml.txt index 9d10d8811..789a71526 100644 --- a/docs/copyright-templates/xml.txt +++ b/docs/copyright-templates/xml.txt @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright 2013 The Android Open Source Project +<!-- Copyright 2015 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. diff --git a/samples/ApiDemos/res/layout/media_projection.xml b/samples/ApiDemos/res/layout/media_projection.xml index 412db4ce2..d9082a3d3 100644 --- a/samples/ApiDemos/res/layout/media_projection.xml +++ b/samples/ApiDemos/res/layout/media_projection.xml @@ -30,6 +30,7 @@ android:orientation="horizontal" android:layout_alignParentBottom="true"> <ToggleButton + android:id="@+id/screen_sharing_toggle" android:text="@string/screen_sharing_toggle" android:layout_width="0dp" android:layout_height="match_parent" diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml index 58daf39fc..65bfba353 100644 --- a/samples/ApiDemos/res/values/strings.xml +++ b/samples/ApiDemos/res/values/strings.xml @@ -790,6 +790,10 @@ <string name="require_encryption">Require encryption</string> <string name="activate_encryption">Activate encryption</string> + <string name="trust_agent_category">Trust Agent Features</string> + <string name="set_trust_agent_component_name">Enabled Component Name</string> + <string name="set_trust_agent_feature_list">Enabled Features (comma-separated)</string> + <!-- Strings used by DeviceAdminSample controller code --> <string name="password_sufficient">Current password meets policy requirements</string> <string name="password_insufficient">Current password does not meet policy requirements</string> diff --git a/samples/ApiDemos/res/xml/device_admin_general.xml b/samples/ApiDemos/res/xml/device_admin_general.xml index cfd0048be..1d0084e6a 100644 --- a/samples/ApiDemos/res/xml/device_admin_general.xml +++ b/samples/ApiDemos/res/xml/device_admin_general.xml @@ -57,4 +57,19 @@ </PreferenceCategory> + <PreferenceCategory + android:title="@string/trust_agent_category" > + + <EditTextPreference + android:key="key_trust_agent_component" + android:title="@string/set_trust_agent_component_name" + android:dialogTitle="@string/set_trust_agent_component_name" /> + + <EditTextPreference + android:key="key_trust_agent_features" + android:title="@string/set_trust_agent_feature_list" + android:dialogTitle="@string/set_trust_agent_feature_list" /> + + </PreferenceCategory> + </PreferenceScreen> diff --git a/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java b/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java index 82df9037a..71badcdf7 100644 --- a/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java +++ b/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.java @@ -26,7 +26,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; +import android.os.PersistableBundle; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; import android.preference.ListPreference; @@ -41,6 +43,8 @@ import android.text.TextUtils; import android.util.Log; import android.widget.Toast; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -71,6 +75,8 @@ public class DeviceAdminSample extends PreferenceActivity { private static final String KEY_DISABLE_NOTIFICATIONS = "key_disable_notifications"; private static final String KEY_DISABLE_UNREDACTED = "key_disable_unredacted"; private static final String KEY_DISABLE_TRUST_AGENTS = "key_disable_trust_agents"; + private static final String KEY_TRUST_AGENT_COMPONENT = "key_trust_agent_component"; + private static final String KEY_TRUST_AGENT_FEATURES = "key_trust_agent_features"; private static final String KEY_DISABLE_KEYGUARD_WIDGETS = "key_disable_keyguard_widgets"; private static final String KEY_DISABLE_KEYGUARD_SECURE_CAMERA = "key_disable_keyguard_secure_camera"; @@ -274,6 +280,8 @@ public class DeviceAdminSample extends PreferenceActivity { private CheckBoxPreference mDisableKeyguardNotificationCheckbox; private CheckBoxPreference mDisableKeyguardTrustAgentCheckbox; private CheckBoxPreference mDisableKeyguardUnredactedCheckbox; + private EditTextPreference mTrustAgentComponent; + private EditTextPreference mTrustAgentFeatures; @Override public void onCreate(Bundle savedInstanceState) { @@ -304,6 +312,14 @@ public class DeviceAdminSample extends PreferenceActivity { mDisableKeyguardTrustAgentCheckbox = (CheckBoxPreference) findPreference(KEY_DISABLE_TRUST_AGENTS); mDisableKeyguardTrustAgentCheckbox.setOnPreferenceChangeListener(this); + + mTrustAgentComponent = + (EditTextPreference) findPreference(KEY_TRUST_AGENT_COMPONENT); + mTrustAgentComponent.setOnPreferenceChangeListener(this); + + mTrustAgentFeatures = + (EditTextPreference) findPreference(KEY_TRUST_AGENT_FEATURES); + mTrustAgentFeatures.setOnPreferenceChangeListener(this); } // At onResume time, reload UI with current values as required @@ -340,8 +356,8 @@ public class DeviceAdminSample extends PreferenceActivity { if (super.onPreferenceChange(preference, newValue)) { return true; } - boolean value = (Boolean) newValue; if (preference == mEnableCheckbox) { + boolean value = (Boolean) newValue; if (value != mAdminActive) { if (value) { // Launch the activity to have the user enable our admin. @@ -359,6 +375,7 @@ public class DeviceAdminSample extends PreferenceActivity { } } } else if (preference == mDisableCameraCheckbox) { + boolean value = (Boolean) newValue; mDPM.setCameraDisabled(mDeviceAdminSample, value); // Delay update because the change is only applied after exiting this method. postReloadSummaries(); @@ -366,20 +383,39 @@ public class DeviceAdminSample extends PreferenceActivity { || preference == mDisableKeyguardSecureCameraCheckbox || preference == mDisableKeyguardNotificationCheckbox || preference == mDisableKeyguardUnredactedCheckbox - || preference == mDisableKeyguardTrustAgentCheckbox) { - // Delay update because the change is only applied after exiting this method. - getView().post(new Runnable() { - @Override - public void run() { - mDPM.setKeyguardDisabledFeatures(mDeviceAdminSample, - createKeyguardDisabledFlag()); - } - }); + || preference == mDisableKeyguardTrustAgentCheckbox + || preference == mTrustAgentComponent + || preference == mTrustAgentFeatures) { + postUpdateDpmDisableFeatures(); postReloadSummaries(); } return true; } + private void postUpdateDpmDisableFeatures() { + getView().post(new Runnable() { + @Override + public void run() { + mDPM.setKeyguardDisabledFeatures(mDeviceAdminSample, + createKeyguardDisabledFlag()); + String component = mTrustAgentComponent.getText(); + if (component != null) { + ComponentName agent = ComponentName.unflattenFromString(component); + if (agent != null) { + String featureString = mTrustAgentFeatures.getText(); + if (featureString != null) { + PersistableBundle bundle = new PersistableBundle(); + bundle.putStringArray("features", featureString.split(",")); + mDPM.setTrustAgentConfiguration(mDeviceAdminSample, agent, bundle); + } + } else { + Log.w(TAG, "Invalid component: " + component); + } + } + } + }); + } + @Override protected void reloadSummaries() { super.reloadSummaries(); @@ -416,6 +452,17 @@ public class DeviceAdminSample extends PreferenceActivity { R.string.keyguard_trust_agents_disabled : R.string.keyguard_trust_agents_enabled); mDisableKeyguardTrustAgentCheckbox.setSummary(keyguardEnableTrustAgentSummary); + + final SharedPreferences prefs = getPreferenceManager().getSharedPreferences(); + final boolean trustDisabled = + (disabled & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0; + String component = prefs.getString(mTrustAgentComponent.getKey(), null); + mTrustAgentComponent.setSummary(component); + mTrustAgentComponent.setEnabled(trustDisabled); + + String features = prefs.getString(mTrustAgentFeatures.getKey(), null); + mTrustAgentFeatures.setSummary(features); + mTrustAgentFeatures.setEnabled(trustDisabled); } /** Updates the device capabilities area (dis/enabling) as the admin is (de)activated */ @@ -426,6 +473,8 @@ public class DeviceAdminSample extends PreferenceActivity { mDisableKeyguardNotificationCheckbox.setEnabled(enabled); mDisableKeyguardUnredactedCheckbox.setEnabled(enabled); mDisableKeyguardTrustAgentCheckbox.setEnabled(enabled); + mTrustAgentComponent.setEnabled(enabled); + mTrustAgentFeatures.setEnabled(enabled); } } diff --git a/samples/ApiDemos/src/com/example/android/apis/media/projection/MediaProjectionDemo.java b/samples/ApiDemos/src/com/example/android/apis/media/projection/MediaProjectionDemo.java index c593daf78..f8a5d0551 100644 --- a/samples/ApiDemos/src/com/example/android/apis/media/projection/MediaProjectionDemo.java +++ b/samples/ApiDemos/src/com/example/android/apis/media/projection/MediaProjectionDemo.java @@ -64,6 +64,7 @@ public class MediaProjectionDemo extends Activity { private VirtualDisplay mVirtualDisplay; private Surface mSurface; private SurfaceView mSurfaceView; + private ToggleButton mToggle; @Override public void onCreate(Bundle savedInstanceState) { @@ -85,6 +86,8 @@ public class MediaProjectionDemo extends Activity { s.setAdapter(arrayAdapter); s.setOnItemSelectedListener(new ResolutionSelector()); s.setSelection(0); + + mToggle = (ToggleButton) findViewById(R.id.screen_sharing_toggle); } @Override @@ -108,6 +111,7 @@ public class MediaProjectionDemo extends Activity { return; } mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data); + mMediaProjection.registerCallback(new MediaProjectionCallback(), null); mVirtualDisplay = createVirtualDisplay(); } @@ -133,11 +137,14 @@ public class MediaProjectionDemo extends Activity { } private void stopScreenSharing() { + if (mToggle.isChecked()) { + mToggle.setChecked(false); + } mScreenSharing = false; - if (mVirtualDisplay == null) { - return; + if (mVirtualDisplay != null) { + mVirtualDisplay.release(); + mVirtualDisplay = null; } - mVirtualDisplay.release(); } private VirtualDisplay createVirtualDisplay() { diff --git a/samples/ApiDemos/src/com/example/android/apis/os/MmsMessagingDemo.java b/samples/ApiDemos/src/com/example/android/apis/os/MmsMessagingDemo.java index 6a5d94c99..0b66d6f5b 100644 --- a/samples/ApiDemos/src/com/example/android/apis/os/MmsMessagingDemo.java +++ b/samples/ApiDemos/src/com/example/android/apis/os/MmsMessagingDemo.java @@ -238,7 +238,8 @@ public class MmsMessagingDemo extends Activity { if (code == Activity.RESULT_OK) { final byte[] response = intent.getByteArrayExtra(SmsManager.EXTRA_MMS_DATA); if (response != null) { - final GenericPdu pdu = new PduParser(response).parse(); + final GenericPdu pdu = new PduParser( + response, PduParserUtil.shouldParseContentDisposition()).parse(); if (pdu instanceof SendConf) { final SendConf sendConf = (SendConf) pdu; if (sendConf.getResponseStatus() == PduHeaders.RESPONSE_STATUS_OK) { @@ -281,7 +282,8 @@ public class MmsMessagingDemo extends Activity { final byte[] response = new byte[nBytes]; final int read = reader.read(response, 0, nBytes); if (read == nBytes) { - final GenericPdu pdu = new PduParser(response).parse(); + final GenericPdu pdu = new PduParser( + response, PduParserUtil.shouldParseContentDisposition()).parse(); if (pdu instanceof RetrieveConf) { final RetrieveConf retrieveConf = (RetrieveConf) pdu; mRecipientsInput.setText(getRecipients(context, retrieveConf)); diff --git a/samples/ApiDemos/src/com/example/android/apis/os/MmsWapPushReceiver.java b/samples/ApiDemos/src/com/example/android/apis/os/MmsWapPushReceiver.java index a291e4aee..f2ca090b0 100644 --- a/samples/ApiDemos/src/com/example/android/apis/os/MmsWapPushReceiver.java +++ b/samples/ApiDemos/src/com/example/android/apis/os/MmsWapPushReceiver.java @@ -16,18 +16,18 @@ package com.example.android.apis.os; -import com.google.android.mms.ContentType; -import com.google.android.mms.pdu.GenericPdu; -import com.google.android.mms.pdu.NotificationInd; -import com.google.android.mms.pdu.PduHeaders; -import com.google.android.mms.pdu.PduParser; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.provider.Telephony; import android.util.Log; +import com.google.android.mms.ContentType; +import com.google.android.mms.pdu.GenericPdu; +import com.google.android.mms.pdu.NotificationInd; +import com.google.android.mms.pdu.PduHeaders; +import com.google.android.mms.pdu.PduParser; + /** * Receiver for MMS WAP push */ @@ -39,7 +39,8 @@ public class MmsWapPushReceiver extends BroadcastReceiver { if (Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION.equals(intent.getAction()) && ContentType.MMS_MESSAGE.equals(intent.getType())) { final byte[] data = intent.getByteArrayExtra("data"); - final PduParser parser = new PduParser(data); + final PduParser parser = new PduParser( + data, PduParserUtil.shouldParseContentDisposition()); GenericPdu pdu = null; try { pdu = parser.parse(); diff --git a/samples/ApiDemos/src/com/example/android/apis/os/PduParserUtil.java b/samples/ApiDemos/src/com/example/android/apis/os/PduParserUtil.java new file mode 100644 index 000000000..541854e6e --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/os/PduParserUtil.java @@ -0,0 +1,37 @@ +/* + * 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. + */ + +package com.example.android.apis.os; + +import android.telephony.SmsManager; + +/** + * Util methods for PduParser + */ +public class PduParserUtil { + /** + * Get the config of whether Content-Disposition header is supported + * for default carrier using new SmsManager API + * + * @return true if supported, false otherwise + */ + public static boolean shouldParseContentDisposition() { + return SmsManager + .getDefault() + .getCarrierConfigValues() + .getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true); + } +} diff --git a/samples/SoftKeyboard/src/com/example/android/softkeyboard/ImePreferences.java b/samples/SoftKeyboard/src/com/example/android/softkeyboard/ImePreferences.java index db6c1d984..14c67b70d 100644 --- a/samples/SoftKeyboard/src/com/example/android/softkeyboard/ImePreferences.java +++ b/samples/SoftKeyboard/src/com/example/android/softkeyboard/ImePreferences.java @@ -41,6 +41,11 @@ public class ImePreferences extends PreferenceActivity { setTitle(R.string.settings_name); } + @Override + protected boolean isValidFragment(final String fragmentName) { + return Settings.class.getName().equals(fragmentName); + } + public static class Settings extends InputMethodSettingsFragment { @Override public void onCreate(Bundle savedInstanceState) { diff --git a/samples/Support7Demos/AndroidManifest.xml b/samples/Support7Demos/AndroidManifest.xml index 4077bb110..1d087a470 100644 --- a/samples/Support7Demos/AndroidManifest.xml +++ b/samples/Support7Demos/AndroidManifest.xml @@ -31,7 +31,7 @@ reading images from the media store from API v19+. --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> - <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="17" /> + <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" /> <!-- The smallest screen this app works on is a phone. The app will scale its UI to larger screens but doesn't make good use of them @@ -86,6 +86,14 @@ </intent-filter> </activity> + <activity android:name=".media.SampleMediaRouteSettingsActivity" + android:label="@string/sample_media_route_settings_activity" + android:theme="@style/Theme.AppCompat.Light"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + </intent-filter> + </activity> + <service android:name=".media.SampleMediaRouteProviderService" android:label="@string/sample_media_route_provider_service" android:process=":mrp"> @@ -130,6 +138,16 @@ </intent-filter> </activity> + <activity android:name=".app.SearchActivity"> + <intent-filter> + <action android:name="android.intent.action.SEARCH" /> + </intent-filter> + + <meta-data android:name="android.app.searchable" + android:resource="@xml/searchable" /> + + </activity> + <activity android:name=".app.ActionBarUsage" android:label="@string/action_bar_usage" android:theme="@style/Theme.AppCompat"> @@ -151,7 +169,7 @@ <activity android:name=".app.ActionBarTabs" android:label="@string/action_bar_tabs" - android:theme="@style/Theme.AppCompat"> + android:theme="@style/Theme.Custom"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="com.example.android.supportv7.SAMPLE_CODE" /> @@ -160,7 +178,7 @@ <activity android:name=".app.ActionBarSettingsActionProviderActivity" android:label="@string/action_bar_settings_action_provider" - android:theme="@style/Theme.AppCompat"> + android:theme="@style/Theme.AppCompat.Light.DarkActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="com.example.android.supportv7.SAMPLE_CODE" /> @@ -177,15 +195,49 @@ </activity> <activity android:name=".app.ActionBarWithDrawerLayout" - android:label="@string/action_bar_with_navigation_drawer" - android:theme="@style/Theme.AppCompat" - > + android:label="@string/action_bar_with_navigation_drawer" + android:theme="@style/Theme.AppCompat" + > + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="com.example.android.supportv7.SAMPLE_CODE"/> + </intent-filter> + </activity> + + <activity android:name=".app.ToolbarUsage" + android:label="@string/toolbar_usage" + android:theme="@style/Theme.Custom.NoActionBar"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.example.android.supportv7.SAMPLE_CODE" /> + </intent-filter> + + <meta-data + android:name="android.app.default_searchable" + android:value=".app.SearchActivity" /> + </activity> + + <activity android:name=".app.ToolbarDisplayOptions" + android:label="@string/toolbar_display_options" + android:theme="@style/Theme.AppCompat.Light.NoActionBar"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.example.android.supportv7.SAMPLE_CODE" /> + </intent-filter> + </activity> + + <activity android:name=".app.ToolbarFragmentPagerMenu" + android:label="@string/toolbar_fragment_pager" + android:theme="@style/Theme.AppCompat.Light.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="com.example.android.supportv7.SAMPLE_CODE" /> </intent-filter> </activity> + <provider android:name=".app.RecentSuggestionsProvider" + android:authorities="com.example.android.supportv7.RecentSuggestionsProvider" /> + <!-- RecyclerView samples --> <activity android:name=".widget.RecyclerViewActivity" android:label="@string/recycler_view" diff --git a/samples/Support7Demos/res/layout/activity_card_view.xml b/samples/Support7Demos/res/layout/activity_card_view.xml index cfd307142..9b985f70e 100644 --- a/samples/Support7Demos/res/layout/activity_card_view.xml +++ b/samples/Support7Demos/res/layout/activity_card_view.xml @@ -123,14 +123,62 @@ android:layout_height="wrap_content" android:layout_toRightOf="@id/alpha_label" android:layout_alignTop="@id/alpha_label"/> - + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="5dp" + android:id="@+id/color_label" + android:textColor="@android:color/black" + android:text="@string/card_view_bg_color" + android:layout_below="@id/alpha_label" + android:layout_alignRight="@id/alpha_label"/> <RadioGroup - android:id="@+id/select_target_radio" + android:id="@+id/select_bg_color_radio" + android:layout_toRightOf="@id/color_label" + android:layout_alignTop="@id/color_label" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_below="@id/alpha_seek_bar"> <RadioButton + android:id="@+id/def" + android:layout_width="40dp" + android:layout_height="wrap_content" + android:background="@color/cardview_light_background" + android:checked="true"/> + <RadioButton + android:id="@+id/yellow" + android:layout_width="40dp" + android:layout_height="wrap_content" + android:background="@color/card_yellow"/> + <RadioButton + android:id="@+id/aquatic" + android:layout_width="40dp" + android:layout_height="wrap_content" + android:background="@color/card_aquatic"/> + <RadioButton + android:id="@+id/classic" + android:layout_width="40dp" + android:layout_height="wrap_content" + android:background="@color/card_classic"/> + <RadioButton + android:id="@+id/sunbrite" + android:layout_width="40dp" + android:layout_height="wrap_content" + android:background="@color/card_sunbrite"/> + <RadioButton + android:id="@+id/tropical" + android:layout_width="40dp" + android:layout_height="wrap_content" + android:background="@color/card_tropical"/> + </RadioGroup> + <RadioGroup + android:id="@+id/select_target_radio" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:layout_below="@id/color_label"> + <RadioButton android:id="@+id/resize_card_view" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -144,7 +192,6 @@ android:textColor="@android:color/black" android:text="@string/card_view_resize_content"/> </RadioGroup> - </RelativeLayout> <RelativeLayout android:layout_width="fill_parent" android:layout_height="match_parent"> @@ -155,9 +202,10 @@ card_view:cardElevation="10dp"> <TextView android:id="@+id/info_text" + android:text="@string/card_view" android:textColor="@android:color/black" - android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> </android.support.v7.widget.CardView> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/card_view_sample_text" diff --git a/samples/Support7Demos/res/layout/toolbar_display_options.xml b/samples/Support7Demos/res/layout/toolbar_display_options.xml new file mode 100644 index 000000000..cc3e66c5a --- /dev/null +++ b/samples/Support7Demos/res/layout/toolbar_display_options.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:minHeight="?attr/actionBarSize" + android:background="?attr/colorPrimary" /> + + <ScrollView + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <Button + android:id="@+id/toggle_home_as_up" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/toggle_home_as_up"/> + + <Button + android:id="@+id/toggle_show_home" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/toggle_show_home"/> + + <Button + android:id="@+id/toggle_use_logo" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/toggle_use_logo"/> + + <Button + android:id="@+id/toggle_show_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/toggle_show_title"/> + + <Button + android:id="@+id/toggle_show_custom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/toggle_show_custom"/> + + <Button + android:id="@+id/cycle_custom_gravity" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/cycle_custom_gravity"/> + + <Button + android:id="@+id/toggle_visibility" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/toggle_visibility"/> + + </LinearLayout> + + </ScrollView> + +</LinearLayout>
\ No newline at end of file diff --git a/samples/Support7Demos/res/layout/toolbar_fragment_pager.xml b/samples/Support7Demos/res/layout/toolbar_fragment_pager.xml new file mode 100644 index 000000000..9f56f194b --- /dev/null +++ b/samples/Support7Demos/res/layout/toolbar_fragment_pager.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="?actionBarSize" + android:background="?attr/colorPrimaryDark" /> + + <android.support.v4.view.ViewPager + android:id="@+id/viewpager" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + +</LinearLayout>
\ No newline at end of file diff --git a/samples/Support7Demos/res/layout/toolbar_usage.xml b/samples/Support7Demos/res/layout/toolbar_usage.xml new file mode 100644 index 000000000..b202e1f68 --- /dev/null +++ b/samples/Support7Demos/res/layout/toolbar_usage.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <android.support.v7.widget.Toolbar + android:id="@+id/toolbar" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:minHeight="?attr/actionBarSize" + android:background="?attr/colorPrimaryDark" + app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" + app:popupTheme="@style/ThemeOverlay.AppCompat.Light" /> + + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="Your content" + android:gravity="center" + android:textAppearance="?android:attr/textAppearanceLarge"/> + +</LinearLayout>
\ No newline at end of file diff --git a/samples/Support7Demos/res/menu/actions.xml b/samples/Support7Demos/res/menu/actions.xml index 38d291ebc..43605fd3c 100644 --- a/samples/Support7Demos/res/menu/actions.xml +++ b/samples/Support7Demos/res/menu/actions.xml @@ -16,9 +16,9 @@ <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/action_search" - android:icon="@android:drawable/ic_menu_search" android:title="@string/action_bar_search" - app:showAsAction="ifRoom" + android:icon="@drawable/abc_ic_search_api_mtrl_alpha" + app:showAsAction="ifRoom|collapseActionView" app:actionViewClass="android.support.v7.widget.SearchView" /> <item android:id="@+id/action_add" android:icon="@android:drawable/ic_menu_add" @@ -30,6 +30,7 @@ <item android:id="@+id/action_share" android:icon="@android:drawable/ic_menu_share" android:title="@string/action_bar_share" + android:enabled="false" app:showAsAction="ifRoom" /> <item android:id="@+id/action_sort" android:icon="@android:drawable/ic_menu_sort_by_size" @@ -38,12 +39,10 @@ <menu> <item android:id="@+id/action_sort_size" android:icon="@android:drawable/ic_menu_sort_by_size" - android:title="@string/action_bar_sort_size" - android:onClick="onSort" /> + android:title="@string/action_bar_sort_size" /> <item android:id="@+id/action_sort_alpha" android:icon="@android:drawable/ic_menu_sort_alphabetically" - android:title="@string/action_bar_sort_alpha" - android:onClick="onSort" /> + android:title="@string/action_bar_sort_alpha" /> </menu> </item> </menu> diff --git a/samples/Support7Demos/res/values/colors.xml b/samples/Support7Demos/res/values/colors.xml index b129b8de6..c8c67a0a2 100644 --- a/samples/Support7Demos/res/values/colors.xml +++ b/samples/Support7Demos/res/values/colors.xml @@ -16,4 +16,9 @@ <resources> <drawable name="blue">#770000ff</drawable> + <color name="card_yellow">#FCF0AD</color> + <color name="card_aquatic">#FCF0AD</color> + <color name="card_classic">#BAB7A9</color> + <color name="card_sunbrite">#F9D6AC</color> + <color name="card_tropical">#56C4E8</color> </resources> diff --git a/samples/Support7Demos/res/values/strings.xml b/samples/Support7Demos/res/values/strings.xml index 14569a53d..4b3a28fd7 100644 --- a/samples/Support7Demos/res/values/strings.xml +++ b/samples/Support7Demos/res/values/strings.xml @@ -25,6 +25,7 @@ <string name="sample_media_router_text">This activity demonstrates how to use MediaRouter from the support library. Select a route from the action bar.</string> <string name="media_route_menu_title">Play on...</string> + <string name="sample_media_route_settings_activity">Sample route settings</string> <string name="library_tab_text">Library</string> <string name="playlist_tab_text">Playlist</string> @@ -121,6 +122,9 @@ necessary. </string> + <string name="toolbar_usage">AppCompat/Toolbar/Toolbar as Action Bar</string> + <string name="toolbar_display_options">AppCompat/Toolbar/Toolbar Display Options</string> + <string name="toolbar_fragment_pager">AppCompat/Toolbar/Toolbar Fragment ViewPager</string> <string name="sample_media_route_provider_remote">Remote Playback (Simulated)</string> <string name="sample_media_route_activity_local">Local Playback</string> @@ -147,6 +151,7 @@ <string name="card_view_radius">Radius</string> <string name="card_view_width">Width</string> <string name="card_view_height">Height</string> + <string name="card_view_bg_color">Background</string> <string name="card_view_elevation">Elevation</string> <string name="card_view_max_elevation">Max Elevation</string> <string name="card_view_alpha">Alpha</string> @@ -156,5 +161,6 @@ <string name="palette">Palette</string> <string name="palette_all_colors">Full color palette</string> + <string name="search_hint">Search...</string> </resources> diff --git a/samples/Support7Demos/res/values/styles.xml b/samples/Support7Demos/res/values/styles.xml new file mode 100644 index 000000000..f085fa5f0 --- /dev/null +++ b/samples/Support7Demos/res/values/styles.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<resources> + + <style name="Theme.Custom" parent="Theme.AppCompat.Light.DarkActionBar"> + <item name="colorPrimary">#ff00bcd4</item> + <item name="colorPrimaryDark">#00838f</item> + <item name="colorAccent">#ffff00</item> + </style> + + <style name="Theme.Custom.NoActionBar" parent="Theme.AppCompat.Light.NoActionBar"> + <item name="colorPrimary">#ff00bcd4</item> + <item name="colorPrimaryDark">#00838f</item> + <item name="colorAccent">#ffff00</item> + </style> + +</resources>
\ No newline at end of file diff --git a/samples/Support7Demos/res/xml/searchable.xml b/samples/Support7Demos/res/xml/searchable.xml new file mode 100644 index 000000000..7f0fa74d5 --- /dev/null +++ b/samples/Support7Demos/res/xml/searchable.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<searchable xmlns:android="http://schemas.android.com/apk/res/android" + android:label="@string/activity_sample_code" + android:hint="@string/search_hint" + android:searchSuggestAuthority="com.example.android.supportv7.RecentSuggestionsProvider" + android:searchSuggestSelection=" ?" />
\ No newline at end of file diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarDisplayOptions.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarDisplayOptions.java index b7b25e8f8..ebda99905 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarDisplayOptions.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarDisplayOptions.java @@ -15,6 +15,8 @@ */ package com.example.android.supportv7.app; +import com.example.android.supportv7.R; + import android.os.Bundle; import android.support.v4.app.FragmentTransaction; import android.support.v7.app.ActionBar; @@ -24,8 +26,8 @@ import android.view.Gravity; import android.view.Menu; import android.view.View; import android.view.ViewGroup.LayoutParams; - -import com.example.android.supportv7.R; +import android.widget.ArrayAdapter; +import android.widget.Toast; /** * This demo shows how various action bar display option flags can be combined and their effects. @@ -59,6 +61,21 @@ public class ActionBarDisplayOptions extends ActionBarActivity bar.addTab(bar.newTab().setText("Tab 1").setTabListener(this)); bar.addTab(bar.newTab().setText("Tab 2").setTabListener(this)); bar.addTab(bar.newTab().setText("Tab 3").setTabListener(this)); + + final ArrayAdapter<String> listAdapter = new ArrayAdapter<String>(bar.getThemedContext(), + R.layout.support_simple_spinner_dropdown_item, + new String[] { "Item 1", "Item 2", "Item 3" }); + bar.setListNavigationCallbacks(listAdapter, new ActionBar.OnNavigationListener() { + @Override + public boolean onNavigationItemSelected(int itemPosition, long itemId) { + Toast.makeText(ActionBarDisplayOptions.this, + listAdapter.getItem(itemPosition), + Toast.LENGTH_SHORT).show(); + return true; + } + }); + + bar.setLogo(R.drawable.ic_media_play); } @Override @@ -94,10 +111,17 @@ public class ActionBarDisplayOptions extends ActionBarActivity flags = ActionBar.DISPLAY_SHOW_CUSTOM; break; case R.id.toggle_navigation: - bar.setNavigationMode( - bar.getNavigationMode() == ActionBar.NAVIGATION_MODE_STANDARD - ? ActionBar.NAVIGATION_MODE_TABS - : ActionBar.NAVIGATION_MODE_STANDARD); + switch (bar.getNavigationMode()) { + case ActionBar.NAVIGATION_MODE_STANDARD: + bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); + break; + case ActionBar.NAVIGATION_MODE_TABS: + bar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); + break; + case ActionBar.NAVIGATION_MODE_LIST: + bar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); + break; + } return; case R.id.cycle_custom_gravity: { ActionBar.LayoutParams lp = mCustomViewLayoutParams; diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarTabs.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarTabs.java index aaa916cf3..4bfea5010 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarTabs.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarTabs.java @@ -36,7 +36,6 @@ public class ActionBarTabs extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.action_bar_tabs); } @@ -61,10 +60,8 @@ public class ActionBarTabs extends ActionBarActivity { if (bar.getNavigationMode() == ActionBar.NAVIGATION_MODE_TABS) { bar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); - bar.setDisplayOptions(ActionBar.DISPLAY_SHOW_TITLE, ActionBar.DISPLAY_SHOW_TITLE); } else { bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE); } } diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarUsage.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarUsage.java index 6ed59fbbe..f8c29caed 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarUsage.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ActionBarUsage.java @@ -67,14 +67,19 @@ public class ActionBarUsage extends ActionBarActivity { @Override public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_sort_alpha: + case R.id.action_sort_size: + onSort(item); + break; + } + Toast.makeText(this, "Selected Item: " + item.getTitle(), Toast.LENGTH_SHORT).show(); + return true; } - // This method is specified as an onClick handler in the menu xml and will - // take precedence over the Activity's onOptionsItemSelected method. - // See res/menu/actions.xml for more info. - public void onSort(MenuItem item) { + private void onSort(MenuItem item) { mSortMode = item.getItemId(); // Request a call to onPrepareOptionsMenu so we can change the sort icon supportInvalidateOptionsMenu(); diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/RecentSuggestionsProvider.java b/samples/Support7Demos/src/com/example/android/supportv7/app/RecentSuggestionsProvider.java new file mode 100644 index 000000000..8d6666d1a --- /dev/null +++ b/samples/Support7Demos/src/com/example/android/supportv7/app/RecentSuggestionsProvider.java @@ -0,0 +1,28 @@ +/* + * 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. + */ + +package com.example.android.supportv7.app; + +import android.content.SearchRecentSuggestionsProvider; + +public class RecentSuggestionsProvider extends SearchRecentSuggestionsProvider { + public final static String AUTHORITY = "com.example.android.supportv7.RecentSuggestionsProvider"; + public final static int MODE = DATABASE_MODE_QUERIES; + + public RecentSuggestionsProvider() { + setupSuggestions(AUTHORITY, MODE); + } +}
\ No newline at end of file diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/SearchActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/app/SearchActivity.java new file mode 100644 index 000000000..622516f7a --- /dev/null +++ b/samples/Support7Demos/src/com/example/android/supportv7/app/SearchActivity.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package com.example.android.supportv7.app; + +import android.app.Activity; +import android.app.SearchManager; +import android.content.Intent; +import android.os.Bundle; +import android.provider.SearchRecentSuggestions; + +/** + * An Activity which is only used for recieving ACTION_SEARCH intents, saving any queries + * to our SearchRecentSuggestions so that SearchView's can display suggestions. + */ +public class SearchActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Get the intent, verify the action and get the query + Intent intent = getIntent(); + if (Intent.ACTION_SEARCH.equals(intent.getAction())) { + String query = intent.getStringExtra(SearchManager.QUERY); + SearchRecentSuggestions suggestions = new SearchRecentSuggestions(this, + RecentSuggestionsProvider.AUTHORITY, RecentSuggestionsProvider.MODE); + suggestions.saveRecentQuery(query, null); + finish(); + } + } +} diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarDisplayOptions.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarDisplayOptions.java new file mode 100644 index 000000000..f5c3d7587 --- /dev/null +++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarDisplayOptions.java @@ -0,0 +1,129 @@ +/* + * 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. + */ + +package com.example.android.supportv7.app; + +import com.example.android.supportv7.R; + +import android.os.Bundle; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBar.Tab; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.Toolbar; +import android.view.Gravity; +import android.view.Menu; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.widget.ArrayAdapter; +import android.widget.Toast; + +/** + * This demo shows how various action bar display option flags can be combined and their effects + * when used on a Toolbar-provided Action Bar + */ +public class ToolbarDisplayOptions extends ActionBarActivity + implements View.OnClickListener { + + private View mCustomView; + private ActionBar.LayoutParams mCustomViewLayoutParams; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.toolbar_display_options); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + findViewById(R.id.toggle_home_as_up).setOnClickListener(this); + findViewById(R.id.toggle_show_home).setOnClickListener(this); + findViewById(R.id.toggle_use_logo).setOnClickListener(this); + findViewById(R.id.toggle_show_title).setOnClickListener(this); + findViewById(R.id.toggle_show_custom).setOnClickListener(this); + findViewById(R.id.cycle_custom_gravity).setOnClickListener(this); + findViewById(R.id.toggle_visibility).setOnClickListener(this); + + // Configure several action bar elements that will be toggled by display options. + mCustomView = getLayoutInflater().inflate(R.layout.action_bar_display_options_custom, null); + mCustomViewLayoutParams = new ActionBar.LayoutParams( + LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.display_options_actions, menu); + return true; + } + + @Override + public boolean onSupportNavigateUp() { + finish(); + return true; + } + + @Override + public void onClick(View v) { + final ActionBar bar = getSupportActionBar(); + int flags = 0; + switch (v.getId()) { + case R.id.toggle_home_as_up: + flags = ActionBar.DISPLAY_HOME_AS_UP; + break; + case R.id.toggle_show_home: + flags = ActionBar.DISPLAY_SHOW_HOME; + break; + case R.id.toggle_use_logo: + flags = ActionBar.DISPLAY_USE_LOGO; + getSupportActionBar().setLogo(R.drawable.ic_media_play); + break; + case R.id.toggle_show_title: + flags = ActionBar.DISPLAY_SHOW_TITLE; + break; + case R.id.toggle_show_custom: + flags = ActionBar.DISPLAY_SHOW_CUSTOM; + break; + case R.id.cycle_custom_gravity: { + ActionBar.LayoutParams lp = mCustomViewLayoutParams; + int newGravity = 0; + switch (lp.gravity & Gravity.HORIZONTAL_GRAVITY_MASK) { + case Gravity.LEFT: + newGravity = Gravity.CENTER_HORIZONTAL; + break; + case Gravity.CENTER_HORIZONTAL: + newGravity = Gravity.RIGHT; + break; + case Gravity.RIGHT: + newGravity = Gravity.LEFT; + break; + } + lp.gravity = lp.gravity & ~Gravity.HORIZONTAL_GRAVITY_MASK | newGravity; + bar.setCustomView(mCustomView, lp); + return; + } + case R.id.toggle_visibility: + if (bar.isShowing()) { + bar.hide(); + } else { + bar.show(); + } + return; + } + + int change = bar.getDisplayOptions() ^ flags; + bar.setDisplayOptions(change, flags); + } +} diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarFragmentPagerMenu.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarFragmentPagerMenu.java new file mode 100644 index 000000000..9b579338c --- /dev/null +++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarFragmentPagerMenu.java @@ -0,0 +1,171 @@ +/* + * 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. + */ + +package com.example.android.supportv7.app; + +import com.example.android.supportv7.R; + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.view.MenuItemCompat; +import android.support.v4.view.ViewPager; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.Toolbar; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.TextView; +import android.widget.Toast; + +import java.util.ArrayList; +import java.util.List; + +/** + * Demonstrates how fragments can participate in the options menu from within a {@link ViewPager}. + */ +public class ToolbarFragmentPagerMenu extends ActionBarActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.toolbar_fragment_pager); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + ViewPager vp = (ViewPager) findViewById(R.id.viewpager); + PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager(), + new MenuFragment(), new Menu2Fragment()); + vp.setAdapter(adapter); + } + + private static class PagerAdapter extends FragmentPagerAdapter { + private final List<Fragment> mFragments; + + public PagerAdapter(FragmentManager fm, Fragment... fragments) { + super(fm); + + mFragments = new ArrayList<Fragment>(); + for (Fragment fragment : fragments) { + mFragments.add(fragment); + } + } + + @Override + public Fragment getItem(int position) { + return mFragments.get(position); + } + + @Override + public int getCount() { + return mFragments.size(); + } + } + + /** + * A fragment that displays a menu. This fragment happens to not + * have a UI (it does not implement onCreateView), but it could also + * have one if it wanted. + */ + public static class MenuFragment extends Fragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + MenuItemCompat.setShowAsAction(menu.add("Menu 1a"), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); + MenuItemCompat.setShowAsAction(menu.add("Menu 1b"), MenuItemCompat.SHOW_AS_ACTION_NEVER); + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + TextView textView = new TextView(container.getContext()); + + textView.setText(getClass().getSimpleName()); + textView.setGravity(Gravity.CENTER); + textView.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + return textView; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getTitle().equals("Menu 1a")) { + Toast.makeText(getActivity(), "Selected Menu 1a.", Toast.LENGTH_SHORT).show(); + return true; + } + if (item.getTitle().equals("Menu 1b")) { + Toast.makeText(getActivity(), "Selected Menu 1b.", Toast.LENGTH_SHORT).show(); + return true; + } + return super.onOptionsItemSelected(item); + } + } + + /** + * Second fragment with a menu. + */ + public static class Menu2Fragment extends Fragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + MenuItemCompat.setShowAsAction(menu.add("Menu 2"), MenuItemCompat.SHOW_AS_ACTION_IF_ROOM); + } + + @Override + public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + TextView textView = new TextView(container.getContext()); + + textView.setText(getClass().getSimpleName()); + textView.setGravity(Gravity.CENTER); + textView.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + return textView; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getTitle().equals("Menu 2")) { + Toast.makeText(getActivity(), "Selected Menu 2.", Toast.LENGTH_SHORT).show(); + return true; + } + return false; + } + } +} diff --git a/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarUsage.java b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarUsage.java new file mode 100644 index 000000000..f5ac03b84 --- /dev/null +++ b/samples/Support7Demos/src/com/example/android/supportv7/app/ToolbarUsage.java @@ -0,0 +1,80 @@ +/* + * 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. + */ +package com.example.android.supportv7.app; + +import com.example.android.supportv7.R; + +import android.app.SearchManager; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.view.ActionMode; +import android.support.v7.widget.PopupMenu; +import android.support.v7.widget.SearchView; +import android.support.v7.widget.Toolbar; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +/** + * This demonstrates idiomatic usage of the Toolbar as the action bar. + */ +public class ToolbarUsage extends ActionBarActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.toolbar_usage); + + // Retrieve the Toolbar from our content view, and set it as the action bar + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.actions, menu); + + // Retrieve the SearchView and plug it into SearchManager + final SearchView searchView = (SearchView) MenuItemCompat + .getActionView(menu.findItem(R.id.action_search)); + + SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE); + searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); + + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + Toast.makeText(this, "Selected Item: " + item.getTitle(), Toast.LENGTH_SHORT).show(); + return true; + } + +} diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/LocalPlayer.java b/samples/Support7Demos/src/com/example/android/supportv7/media/LocalPlayer.java index 806df257d..b3c14c26b 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/media/LocalPlayer.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/media/LocalPlayer.java @@ -28,6 +28,7 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.SystemClock; +import android.support.v4.media.session.MediaSessionCompat; import android.support.v7.media.MediaRouter.RouteInfo; import android.support.v7.media.MediaItemStatus; import android.util.Log; @@ -56,12 +57,6 @@ public abstract class LocalPlayer extends Player implements private static final String TAG = "LocalPlayer"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private static final int STATE_IDLE = 0; - private static final int STATE_PLAY_PENDING = 1; - private static final int STATE_READY = 2; - private static final int STATE_PLAYING = 3; - private static final int STATE_PAUSED = 4; - private final Context mContext; private final Handler mHandler = new Handler(); private MediaPlayer mMediaPlayer; @@ -109,6 +104,11 @@ public abstract class LocalPlayer extends Player implements } } + @Override + public MediaSessionCompat getMediaSession() { + return mMediaSession; + } + // Player @Override public void play(final PlaylistItem item) { diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/Player.java b/samples/Support7Demos/src/com/example/android/supportv7/media/Player.java index 32b128588..fcab57dd9 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/media/Player.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/media/Player.java @@ -16,11 +16,17 @@ package com.example.android.supportv7.media; -import android.net.Uri; +import android.app.PendingIntent; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.graphics.Bitmap; +import android.support.v4.media.MediaMetadataCompat; +import android.support.v4.media.session.MediaSessionCompat; +import android.support.v4.media.session.PlaybackStateCompat; import android.support.v7.media.MediaControlIntent; import android.support.v7.media.MediaRouter.RouteInfo; +import android.util.Log; /** * Abstraction of common playback operations of media items, such as play, @@ -28,7 +34,18 @@ import android.support.v7.media.MediaRouter.RouteInfo; * of media items. */ public abstract class Player { + private static final String TAG = "SampleMediaRoutePlayer"; + protected static final int STATE_IDLE = 0; + protected static final int STATE_PLAY_PENDING = 1; + protected static final int STATE_READY = 2; + protected static final int STATE_PLAYING = 3; + protected static final int STATE_PAUSED = 4; + + private static final long PLAYBACK_ACTIONS = PlaybackStateCompat.ACTION_PAUSE + | PlaybackStateCompat.ACTION_PLAY; + protected Callback mCallback; + protected MediaSessionCompat mMediaSession; public abstract boolean isRemotePlayback(); public abstract boolean isQueuingSupported(); @@ -61,7 +78,7 @@ public abstract class Player { mCallback = callback; } - public static Player create(Context context, RouteInfo route) { + public static Player create(Context context, RouteInfo route, MediaSessionCompat session) { Player player; if (route != null && route.supportsControlCategory( MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { @@ -71,10 +88,62 @@ public abstract class Player { } else { player = new LocalPlayer.OverlayPlayer(context); } + player.initMediaSession(session); player.connect(route); return player; } + public MediaSessionCompat getMediaSession() { + return mMediaSession; + } + + protected void updateMetadata() { + if (mMediaSession == null) { + return; + } + MediaMetadataCompat.Builder bob = new MediaMetadataCompat.Builder(); + bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, getDescription()); + bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Subtitle of the thing"); + bob.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_DESCRIPTION, + "Description of the thing"); + bob.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, getSnapshot()); + mMediaSession.setMetadata(bob.build()); + } + + protected void publishState(int state) { + if (mMediaSession == null) { + return; + } + PlaybackStateCompat.Builder bob = new PlaybackStateCompat.Builder(); + bob.setActions(PLAYBACK_ACTIONS); + switch (state) { + case STATE_PLAYING: + bob.setState(PlaybackStateCompat.STATE_PLAYING, -1, 1); + break; + case STATE_READY: + case STATE_PAUSED: + bob.setState(PlaybackStateCompat.STATE_PAUSED, -1, 0); + break; + case STATE_IDLE: + bob.setState(PlaybackStateCompat.STATE_STOPPED, -1, 0); + break; + } + PlaybackStateCompat pbState = bob.build(); + Log.d(TAG, "Setting state to " + pbState); + mMediaSession.setPlaybackState(pbState); + if (state != STATE_IDLE) { + mMediaSession.setActive(true); + } else { + mMediaSession.setActive(false); + } + } + + private void initMediaSession(MediaSessionCompat session) { + mMediaSession = session; + updateMetadata(); + } + + public interface Callback { void onError(); void onCompletion(); diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/RemotePlayer.java b/samples/Support7Demos/src/com/example/android/supportv7/media/RemotePlayer.java index 5020c371b..d47c26069 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/media/RemotePlayer.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/media/RemotePlayer.java @@ -140,6 +140,9 @@ public class RemotePlayer extends Player { } if (item.getState() == MediaItemStatus.PLAYBACK_STATE_PAUSED) { pause(); + publishState(STATE_PAUSED); + } else { + publishState(STATE_PLAYING); } if (mCallback != null) { mCallback.onPlaylistChanged(); @@ -214,6 +217,7 @@ public class RemotePlayer extends Player { if (mCallback != null) { mCallback.onPlaylistChanged(); } + publishState(STATE_PAUSED); } @Override @@ -239,6 +243,7 @@ public class RemotePlayer extends Player { if (mCallback != null) { mCallback.onPlaylistChanged(); } + publishState(STATE_PLAYING); } @Override @@ -254,6 +259,7 @@ public class RemotePlayer extends Player { // ignore if no session return; } + publishState(STATE_IDLE); if (DEBUG) { Log.d(TAG, "stop"); } diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouteControllerDialog.java b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouteControllerDialog.java deleted file mode 100644 index a2cacc3f0..000000000 --- a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouteControllerDialog.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2013 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. - */ - -package com.example.android.supportv7.media; - -import com.example.android.supportv7.R; - -import android.app.Dialog; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.support.v7.app.MediaRouteControllerDialog; -import android.support.v7.media.MediaRouteSelector; -import android.support.v7.media.MediaRouter; -import android.util.Log; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -/** - * This class serves as an example on how to customize the media router control - * dialog. It is derived from the standard MediaRouteControllerDialog with the - * following overrides: - * - * 1. Shows thumbnail/snapshot of the current item - * - * 2. For variable volume routes, only allow volume control via Volume Up/Down - * keys (to prevent accidental tapping on the volume adjust seekbar that sets - * volume to maximum) - * - * 3. Provides transport control buttons (play/pause, stop) - */ -public class SampleMediaRouteControllerDialog extends MediaRouteControllerDialog { - private static final String TAG = "SampleMediaRouteControllerDialog"; - private final SampleMediaRouterActivity mActivity; - private final SessionManager mSessionManager; - private final Player mPlayer; - private ImageButton mPauseResumeButton; - private ImageButton mStopButton; - private ImageView mThumbnail; - private TextView mTextView; - private LinearLayout mInfoLayout; - private LinearLayout mVolumeLayout; - - public SampleMediaRouteControllerDialog(Context context, - SessionManager manager, Player player) { - super(context); - mActivity = (SampleMediaRouterActivity) context; - mSessionManager = manager; - mPlayer = player; - } - - @Override - public View onCreateMediaControlView(Bundle savedInstanceState) { - // Thumbnail and Track info - View v = getLayoutInflater().inflate(R.layout.sample_media_controller, null); - mInfoLayout = (LinearLayout)v.findViewById(R.id.media_route_info); - mTextView = (TextView)v.findViewById(R.id.track_info); - mThumbnail = (ImageView)v.findViewById(R.id.snapshot); - - // Transport controls - mPauseResumeButton = (ImageButton)v.findViewById(R.id.pause_resume_button); - mPauseResumeButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mActivity != null) { - mActivity.handleMediaKey(new KeyEvent(KeyEvent.ACTION_DOWN, - KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)); - } - } - }); - - mStopButton = (ImageButton)v.findViewById(R.id.stop_button); - mStopButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mActivity != null) { - mActivity.handleMediaKey(new KeyEvent(KeyEvent.ACTION_DOWN, - KeyEvent.KEYCODE_MEDIA_STOP)); - } - } - }); - - // update session status (will callback to updateUi at the end) - mSessionManager.updateStatus(); - return v; - } - - public void updateUi() { - String trackInfo = mPlayer.getDescription(); - Bitmap snapshot = mPlayer.getSnapshot(); - if (mPlayer.isRemotePlayback() && !trackInfo.isEmpty() && snapshot != null) { - mInfoLayout.setVisibility(View.VISIBLE); - mThumbnail.setImageBitmap(snapshot); - mTextView.setText(trackInfo); - } else { - mInfoLayout.setVisibility(View.GONE); - } - // show pause or resume icon depending on current state - mPauseResumeButton.setImageResource(mSessionManager.isPaused() ? - R.drawable.ic_media_play : R.drawable.ic_media_pause); - } -} diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouteProvider.java b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouteProvider.java index 8a205649f..15cf19b7d 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouteProvider.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouteProvider.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter.MalformedMimeTypeException; +import android.content.IntentSender; import android.content.res.Resources; import android.graphics.Bitmap; import android.media.AudioManager; @@ -173,6 +174,10 @@ final class SampleMediaRouteProvider extends MediaRouteProvider { private void publishRoutes() { Resources r = getContext().getResources(); + Intent settingsIntent = new Intent(Intent.ACTION_MAIN); + settingsIntent.setClass(getContext(), SampleMediaRouteSettingsActivity.class); + IntentSender is = PendingIntent.getActivity(getContext(), 99, settingsIntent, + Intent.FLAG_ACTIVITY_NEW_TASK).getIntentSender(); MediaRouteDescriptor routeDescriptor1 = new MediaRouteDescriptor.Builder( FIXED_VOLUME_ROUTE_ID, @@ -183,6 +188,8 @@ final class SampleMediaRouteProvider extends MediaRouteProvider { .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE) .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_FIXED) .setVolume(VOLUME_MAX) + .setCanDisconnect(true) + .setSettingsActivity(is) .build(); MediaRouteDescriptor routeDescriptor2 = new MediaRouteDescriptor.Builder( @@ -195,6 +202,7 @@ final class SampleMediaRouteProvider extends MediaRouteProvider { .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) .setVolumeMax(VOLUME_MAX) .setVolume(mVolume) + .setSettingsActivity(is) .build(); MediaRouteDescriptor routeDescriptor3 = new MediaRouteDescriptor.Builder( @@ -207,6 +215,7 @@ final class SampleMediaRouteProvider extends MediaRouteProvider { .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) .setVolumeMax(VOLUME_MAX) .setVolume(mVolume) + .setCanDisconnect(true) .build(); MediaRouteDescriptor routeDescriptor4 = new MediaRouteDescriptor.Builder( @@ -239,7 +248,7 @@ final class SampleMediaRouteProvider extends MediaRouteProvider { public SampleRouteController(String routeId) { mRouteId = routeId; - mPlayer = Player.create(getContext(), null); + mPlayer = Player.create(getContext(), null, null); mSessionManager.setPlayer(mPlayer); mSessionManager.setCallback(new SessionManager.Callback() { @Override diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouteSettingsActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouteSettingsActivity.java new file mode 100644 index 000000000..a1d07fb56 --- /dev/null +++ b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouteSettingsActivity.java @@ -0,0 +1,27 @@ +/* + * 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. + */ + +package com.example.android.supportv7.media; + +import android.support.v7.app.ActionBarActivity; + +/** + * This activity is a dummy settings activity for the + * {@link SampleMediaRouteProvider}. + */ +public class SampleMediaRouteSettingsActivity extends ActionBarActivity { + +} diff --git a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java index dfa14163c..ba1949965 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/media/SampleMediaRouterActivity.java @@ -20,6 +20,7 @@ import com.example.android.supportv7.R; import android.content.ComponentName; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.app.PendingIntent; @@ -34,9 +35,11 @@ import android.os.Handler; import android.os.Bundle; import android.os.SystemClock; import android.support.v4.app.FragmentManager; +import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBarActivity; import android.support.v7.app.MediaRouteActionProvider; +import android.support.v7.app.MediaRouteButton; import android.support.v7.app.MediaRouteControllerDialog; import android.support.v7.app.MediaRouteControllerDialogFragment; import android.support.v7.app.MediaRouteDiscoveryFragment; @@ -66,6 +69,7 @@ import android.widget.TabHost.TabSpec; import android.widget.TabHost.OnTabChangeListener; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; + import java.io.File; /** @@ -93,7 +97,6 @@ public class SampleMediaRouterActivity extends ActionBarActivity { private SeekBar mSeekBar; private boolean mNeedResume; private boolean mSeeking; - private SampleMediaRouteControllerDialog mControllerDialog; private final Handler mHandler = new Handler(); private final Runnable mUpdateSeekRunnable = new Runnable() { @@ -130,19 +133,18 @@ public class SampleMediaRouterActivity extends ActionBarActivity { public void onRouteSelected(MediaRouter router, RouteInfo route) { Log.d(TAG, "onRouteSelected: route=" + route); - mPlayer = Player.create(SampleMediaRouterActivity.this, route); + mPlayer = Player.create(SampleMediaRouterActivity.this, route, mMediaSession); mPlayer.updatePresentation(); mSessionManager.setPlayer(mPlayer); mSessionManager.unsuspend(); - registerRCC(); updateUi(); } @Override public void onRouteUnselected(MediaRouter router, RouteInfo route) { Log.d(TAG, "onRouteUnselected: route=" + route); - unregisterRCC(); + mMediaSession.setActive(false); PlaylistItem item = getCheckedPlaylistItem(); if (item != null) { @@ -152,7 +154,6 @@ public class SampleMediaRouterActivity extends ActionBarActivity { } mPlayer.updatePresentation(); mPlayer.release(); - mControllerDialog = null; } @Override @@ -183,7 +184,7 @@ public class SampleMediaRouterActivity extends ActionBarActivity { } }; - private RemoteControlClient mRemoteControlClient; + private MediaSessionCompat mMediaSession; private ComponentName mEventReceiver; private AudioManager mAudioManager; private PendingIntent mMediaPendingIntent; @@ -367,11 +368,13 @@ public class SampleMediaRouterActivity extends ActionBarActivity { mMediaPendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0); // Create and register the remote control client - registerRCC(); + createMediaSession(); + mMediaRouter.setMediaSessionCompat(mMediaSession); // Set up playback manager and player mPlayer = Player.create(SampleMediaRouterActivity.this, - mMediaRouter.getSelectedRoute()); + mMediaRouter.getSelectedRoute(), mMediaSession); + mSessionManager.setPlayer(mPlayer); mSessionManager.setCallback(new SessionManager.Callback() { @Override @@ -387,40 +390,42 @@ public class SampleMediaRouterActivity extends ActionBarActivity { updateUi(); } - private void registerRCC() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - // Create the RCC and register with AudioManager and MediaRouter - mAudioManager.requestAudioFocus(mAfChangeListener, - AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); - mAudioManager.registerMediaButtonEventReceiver(mEventReceiver); - mRemoteControlClient = new RemoteControlClient(mMediaPendingIntent); - mAudioManager.registerRemoteControlClient(mRemoteControlClient); - mMediaRouter.addRemoteControlClient(mRemoteControlClient); - SampleMediaButtonReceiver.setActivity(SampleMediaRouterActivity.this); - mRemoteControlClient.setTransportControlFlags( - RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE); - mRemoteControlClient.setPlaybackState( - RemoteControlClient.PLAYSTATE_PLAYING); - } - } + private void createMediaSession() { + // Create the MediaSession + mMediaSession = new MediaSessionCompat(this, "SampleMediaRouter", mEventReceiver, + mMediaPendingIntent); + mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS + | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); + mMediaSession.setCallback(new MediaSessionCompat.Callback() { + @Override + public boolean onMediaButtonEvent(Intent mediaButtonEvent) { + if (mediaButtonEvent != null) { + return handleMediaKey( + (KeyEvent) mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)); + } + return super.onMediaButtonEvent(mediaButtonEvent); + } - private void unregisterRCC() { - // Unregister the RCC with AudioManager and MediaRouter - if (mRemoteControlClient != null) { - mRemoteControlClient.setTransportControlFlags(0); - mAudioManager.abandonAudioFocus(mAfChangeListener); - mAudioManager.unregisterMediaButtonEventReceiver(mEventReceiver); - mAudioManager.unregisterRemoteControlClient(mRemoteControlClient); - mMediaRouter.removeRemoteControlClient(mRemoteControlClient); - SampleMediaButtonReceiver.setActivity(null); - mRemoteControlClient = null; - } + @Override + public void onPlay() { + mSessionManager.resume(); + } + + @Override + public void onPause() { + mSessionManager.pause(); + } + }); + + SampleMediaButtonReceiver.setActivity(SampleMediaRouterActivity.this); } public boolean handleMediaKey(KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { + if (event != null && event.getAction() == KeyEvent.ACTION_DOWN + && event.getRepeatCount() == 0) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_HEADSETHOOK: { Log.d(TAG, "Received Play/Pause event from RemoteControlClient"); if (mSessionManager.isPaused()) { @@ -497,11 +502,9 @@ public class SampleMediaRouterActivity extends ActionBarActivity { @Override public void onDestroy() { - // Unregister the remote control client - unregisterRCC(); - mSessionManager.stop(); mPlayer.release(); + mMediaSession.release(); super.onDestroy(); } @@ -520,15 +523,7 @@ public class SampleMediaRouterActivity extends ActionBarActivity { mediaRouteActionProvider.setDialogFactory(new MediaRouteDialogFactory() { @Override public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() { - return new MediaRouteControllerDialogFragment() { - @Override - public MediaRouteControllerDialog onCreateControllerDialog( - Context context, Bundle savedInstanceState) { - mControllerDialog = new SampleMediaRouteControllerDialog( - context, mSessionManager, mPlayer); - return mControllerDialog; - } - }; + return new ControllerDialogFragment(mPlayer); } }); @@ -564,8 +559,8 @@ public class SampleMediaRouterActivity extends ActionBarActivity { updatePlaylist(); updateRouteDescription(); updateButtons(); - if (mControllerDialog != null) { - mControllerDialog.updateUi(); + if (mPlayer != null) { + mPlayer.updateMetadata(); } } @@ -593,11 +588,6 @@ public class SampleMediaRouterActivity extends ActionBarActivity { // only enable seek bar when duration is known PlaylistItem item = getCheckedPlaylistItem(); mSeekBar.setEnabled(item != null && item.getDuration() > 0); - if (mRemoteControlClient != null) { - mRemoteControlClient.setPlaybackState(mSessionManager.isPaused() ? - RemoteControlClient.PLAYSTATE_PAUSED : - RemoteControlClient.PLAYSTATE_PLAYING); - } } private PlaylistItem getCheckedPlaylistItem() { @@ -745,4 +735,35 @@ public class SampleMediaRouterActivity extends ActionBarActivity { */ public static class LightWithDarkActionBar extends SampleMediaRouterActivity { } + + public static class ControllerDialogFragment extends MediaRouteControllerDialogFragment { + private MediaRouteControllerDialog mControllerDialog; + private Player mPlayer; + + public ControllerDialogFragment() { + super(); + } + + public ControllerDialogFragment(Player player) { + mPlayer = player; + } + + @Override + public MediaRouteControllerDialog onCreateControllerDialog( + Context context, Bundle savedInstanceState) { + mControllerDialog = super.onCreateControllerDialog(context, + savedInstanceState); + mControllerDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + mControllerDialog = null; + } + }); + return mControllerDialog; + } + + public void setPlayer(Player player) { + mPlayer = player; + } + } } diff --git a/samples/Support7Demos/src/com/example/android/supportv7/view/CardViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/view/CardViewActivity.java index ba4f0cb1d..ca0c08aac 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/view/CardViewActivity.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/view/CardViewActivity.java @@ -16,6 +16,8 @@ package com.example.android.supportv7.view; import android.app.Activity; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.v4.view.ViewCompat; @@ -25,8 +27,11 @@ import android.view.ViewGroup; import android.widget.RadioGroup; import android.widget.SeekBar; import android.widget.TextView; + import com.example.android.supportv7.R; +import java.lang.reflect.Field; + public class CardViewActivity extends Activity { CardView mCardView; @@ -84,7 +89,7 @@ public class CardViewActivity extends Activity { lp = setViewBounds(mInfoText); } mInfoText.setText("radius: " + mCornerRadiusSeekBar.getProgress() - +", alpha: " + mAlphaSeekBar.getProgress() + + ", alpha: " + mAlphaSeekBar.getProgress() + "\n w: " + lp.width + "\nh: " + lp.height + "\nelevation: " + mCardView.getCardElevation() + " of " + mCardView.getMaxCardElevation()); @@ -143,15 +148,42 @@ public class CardViewActivity extends Activity { } }); - update(); new Handler().postDelayed(new Runnable() { @Override public void run() { View content = findViewById(android.R.id.content); + mWidthSeekBar.setProgress(mCardView.getWidth()); + mHeightSeekBar.setProgress(mCardView.getHeight()); mWidthSeekBar.setMax(content.getWidth()); mHeightSeekBar.setMax(content.getHeight()); + update(); } }, 100); + + ((RadioGroup) findViewById(R.id.select_bg_color_radio)) + .setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + mCardView.setCardBackgroundColor( + getResources().getColor(getColorId(checkedId))); + } + }); } + private int getColorId(int id) { + switch (id) { + case R.id.yellow: + return R.color.card_yellow; + case R.id.aquatic: + return R.color.card_aquatic; + case R.id.classic: + return R.color.card_classic; + case R.id.sunbrite: + return R.color.card_sunbrite; + case R.id.tropical: + return R.color.card_tropical; + default: + return R.color.cardview_light_background; + } + } } diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java index 50fe1e1a8..4c56933e7 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/AnimatedRecyclerView.java @@ -124,8 +124,12 @@ public class AnimatedRecyclerView extends Activity { public void itemClicked(View view) { ViewGroup parent = (ViewGroup) view; MyViewHolder holder = (MyViewHolder) mRecyclerView.getChildViewHolder(parent); + final int position = holder.getAdapterPosition(); + if (position == RecyclerView.NO_POSITION) { + return; + } mAdapter.toggleExpanded(holder); - mAdapter.notifyItemChanged(holder.getPosition()); + mAdapter.notifyItemChanged(position); } public void deleteItem(View view) { @@ -240,7 +244,7 @@ public class AnimatedRecyclerView extends Activity { if (lastVisibleView != null) { RecyclerView.LayoutParams lastParams = (RecyclerView.LayoutParams) lastVisibleView.getLayoutParams(); - int lastPosition = lastParams.getViewPosition(); + int lastPosition = lastParams.getViewLayoutPosition(); final List<RecyclerView.ViewHolder> previousViews = recycler.getScrapList(); count = previousViews.size(); for (int i = 0; i < count; ++i) { @@ -250,7 +254,7 @@ public class AnimatedRecyclerView extends Activity { if (params.isItemRemoved()) { continue; } - int position = params.getViewPosition(); + int position = params.getViewLayoutPosition(); int newTop; if (position < mFirstPosition) { newTop = view.getHeight() * (position - mFirstPosition); @@ -480,7 +484,7 @@ public class AnimatedRecyclerView extends Activity { } public void selectItem(MyViewHolder holder, boolean selected) { - mSelected.put((String) holder.textView.getText(), selected); + mSelected.put((String) holder.textView.getText().toString(), selected); } public void toggleExpanded(MyViewHolder holder) { diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java index 0a6ac6d36..4bb64f4b1 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/BaseLayoutManagerActivity.java @@ -84,11 +84,10 @@ abstract public class BaseLayoutManagerActivity<T extends RecyclerView.LayoutMan vh.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - final int pos = vh.getPosition(); - if (pos + 1 < getItemCount()) { + final int pos = vh.getAdapterPosition(); + if (pos != RecyclerView.NO_POSITION && pos + 1 < getItemCount()) { swap(pos, pos + 1); } - notifyItemChanged(pos); } }); return vh; diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/GridLayoutManagerActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/GridLayoutManagerActivity.java index 00cfbf86a..ef25c0b0e 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/widget/GridLayoutManagerActivity.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/GridLayoutManagerActivity.java @@ -119,11 +119,13 @@ public class GridLayoutManagerActivity extends BaseLayoutManagerActivity<GridLay vh.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - final int pos = vh.getPosition(); + final int pos = vh.getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) { + return; + } if (pos + 1 < getItemCount()) { swap(pos, pos + 1); } - notifyItemChanged(pos); } }); return vh; diff --git a/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java b/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java index 747992b19..38b50bd3a 100644 --- a/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java +++ b/samples/Support7Demos/src/com/example/android/supportv7/widget/RecyclerViewActivity.java @@ -56,7 +56,10 @@ public class RecyclerViewActivity extends Activity { vh.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - final int pos = vh.getPosition(); + final int pos = vh.getAdapterPosition(); + if (pos == RecyclerView.NO_POSITION) { + return; + } if (pos + 1 < getItemCount()) { swap(pos, pos + 1); } diff --git a/samples/SupportLeanbackDemos/AndroidManifest.xml b/samples/SupportLeanbackDemos/AndroidManifest.xml index 95f7a2c0e..391bcb752 100644 --- a/samples/SupportLeanbackDemos/AndroidManifest.xml +++ b/samples/SupportLeanbackDemos/AndroidManifest.xml @@ -10,10 +10,12 @@ android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:banner="@drawable/ic_launcher" + android:supportsRtl="true" android:theme="@style/Theme.Example.Leanback"> <activity android:name="MainActivity" - android:label="@string/app_name"> + android:label="@string/app_name" + android:theme="@style/Theme.Example.Leanback.Browse"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LEANBACK_LAUNCHER" /> @@ -25,6 +27,11 @@ </activity> <activity android:name="DetailsActivity" + android:theme="@style/Theme.Example.Leanback.Details" + android:exported="true" /> + + <activity android:name="RowsActivity" + android:theme="@style/Theme.Example.Leanback.Rows" android:exported="true" /> <activity android:name="PlaybackOverlayActivity" @@ -39,5 +46,7 @@ <activity android:name="BrowseErrorActivity" android:exported="true" /> + <activity android:name="HorizontalGridTestActivity" + android:exported="true" /> </application> </manifest> diff --git a/samples/SupportLeanbackDemos/res/layout/horizontal_grid.xml b/samples/SupportLeanbackDemos/res/layout/horizontal_grid.xml new file mode 100644 index 000000000..7faea03f7 --- /dev/null +++ b/samples/SupportLeanbackDemos/res/layout/horizontal_grid.xml @@ -0,0 +1,23 @@ +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:lb="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <android.support.v17.leanback.widget.HorizontalGridView + android:id="@+id/gridview" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipToPadding="false" + android:focusable="true" + android:focusableInTouchMode="true" + android:background="#00ffff" + lb:horizontalMargin="12dip" + lb:verticalMargin="24dip" + lb:numberOfRows="3" + lb:rowHeight="150dip" + android:paddingBottom="12dip" + android:paddingLeft="12dip" + android:paddingRight="12dip" + android:paddingTop="12dip" /> +</RelativeLayout> diff --git a/samples/SupportLeanbackDemos/res/layout/rows.xml b/samples/SupportLeanbackDemos/res/layout/rows.xml new file mode 100644 index 000000000..d77f7cacc --- /dev/null +++ b/samples/SupportLeanbackDemos/res/layout/rows.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/rows_frame" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <TextView + android:id="@+id/rows_title" + android:text="RowsFragment" + style="?attr/browseTitleTextStyle" + android:paddingStart="?attr/browsePaddingStart" + android:paddingEnd="?attr/browsePaddingEnd" + android:paddingTop="?attr/browsePaddingTop" + android:paddingBottom="10dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + <fragment + android:name="com.example.android.leanback.RowsFragment" + android:id="@+id/main_rows_fragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + /> + +</FrameLayout> diff --git a/samples/SupportLeanbackDemos/res/values/styles.xml b/samples/SupportLeanbackDemos/res/values/styles.xml new file mode 100644 index 000000000..17ea0efad --- /dev/null +++ b/samples/SupportLeanbackDemos/res/values/styles.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style name="Widget.Example.Leanback.Title.Text" parent="Widget.Leanback.Title.Text" > + </style> + <style name="Widget.Example.Leanback.Rows.VerticalGridView" parent="Widget.Leanback.Rows.VerticalGridView"> + <item name="android:paddingTop">96dp</item> + </style> +</resources>
\ No newline at end of file diff --git a/samples/SupportLeanbackDemos/res/values/themes.xml b/samples/SupportLeanbackDemos/res/values/themes.xml index 121e25cca..22a41f06d 100644 --- a/samples/SupportLeanbackDemos/res/values/themes.xml +++ b/samples/SupportLeanbackDemos/res/values/themes.xml @@ -1,13 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. +--> <resources> <style name="Theme.Example.Leanback" parent="Theme.Leanback"> -<!-- uncomment to override default transition settings: - <item name="android:windowEnterTransition">@android:transition/fade</item> - <item name="android:windowExitTransition">@android:transition/fade</item> - <item name="android:windowSharedElementExitTransition">@android:transition/move</item> - <item name="android:windowSharedElementEnterTransition">@android:transition/move</item> - <item name="android:windowAllowExitTransitionOverlap">true</item> - <item name="android:windowAllowEnterTransitionOverlap">false</item> - <item name="android:windowContentTransitions">true</item> - --> + </style> + <style name="Theme.Example.Leanback.Browse" parent="Theme.Leanback.Browse"> + </style> + <style name="Theme.Example.Leanback.Details" parent="Theme.Leanback.Details"> + </style> + <style name="Theme.Example.Leanback.Rows" parent="Theme.Leanback"> + <item name="browseTitleTextStyle">@style/Widget.Example.Leanback.Title.Text</item> + <item name="rowsVerticalGridStyle">@style/Widget.Example.Leanback.Rows.VerticalGridView</item> </style> </resources> diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseAnimationFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseAnimationFragment.java index e6b92c46b..3883081c9 100644 --- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseAnimationFragment.java +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseAnimationFragment.java @@ -23,6 +23,7 @@ import android.support.v17.leanback.widget.OnItemClickedListener; import android.support.v17.leanback.widget.Row; import android.util.Log; import android.view.View; +import android.os.Handler; import java.util.Random; @@ -76,13 +77,13 @@ public class BrowseAnimationFragment extends mRowsAdapter = new ArrayObjectAdapter(lrp); for (int i = 0; i < NUM_ROWS; ++i) { mRowsAdapter.add( - createRandomRow(new HeaderItem(i, "Row " + i, null))); + createRandomRow(new HeaderItem(i, "Row " + i))); } setAdapter(mRowsAdapter); } Item createRandomItem() { - switch (sRand.nextInt(13)) { + switch (sRand.nextInt(15)) { default: case 0: return new Item("Remove Item before", new OnItemClickedListener() { @@ -198,7 +199,7 @@ public class BrowseAnimationFragment extends if (index >= 0) { int headerId = sRand.nextInt(); mRowsAdapter.add(index, createRandomRow(new HeaderItem( - headerId, "Row " + headerId, null))); + headerId, "Row " + headerId))); } } }); @@ -211,7 +212,7 @@ public class BrowseAnimationFragment extends int headerId = sRand.nextInt(); mRowsAdapter.add( index + 1, createRandomRow(new HeaderItem( - headerId, "Row " + headerId, null))); + headerId, "Row " + headerId))); } } }); @@ -242,6 +243,35 @@ public class BrowseAnimationFragment extends } } }); + case 13: + return new Item("Replace Item before", new OnItemClickedListener() { + @Override + public void onItemClicked(Object item, Row row) { + ArrayObjectAdapter adapter = ((ArrayObjectAdapter) ((ListRow) row) + .getAdapter()); + int index = adapter.indexOf(item); + if (index >= 0) { + if (index > 0) + index--; + adapter.replace(index, createRandomItem()); + } + } + }); + case 14: + return new Item("Remove all then re-add", new OnItemClickedListener() { + @Override + public void onItemClicked(Object item, Row row) { + final ArrayObjectAdapter adapter = ((ArrayObjectAdapter) ((ListRow) row) + .getAdapter()); + adapter.clear(); + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + adapter.add(0, createRandomItem()); + } + }, 1000); + } + }); } } diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java index a35ab5ef7..f2f61925f 100644 --- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseErrorActivity.java @@ -50,6 +50,9 @@ public class BrowseErrorActivity extends Activity handler.postDelayed(new Runnable() { @Override public void run() { + if (getFragmentManager().isDestroyed()) { + return; + } getFragmentManager().beginTransaction().remove(mSpinnerFragment).commit(); mErrorFragment.setErrorContent(getResources()); } diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java index abf82e385..91fec5fdc 100644 --- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/BrowseFragment.java @@ -15,6 +15,7 @@ package com.example.android.leanback; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; import android.support.v4.app.ActivityOptionsCompat; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.HeaderItem; @@ -33,6 +34,7 @@ import android.view.ViewGroup; public class BrowseFragment extends android.support.v17.leanback.app.BrowseFragment { private static final String TAG = "leanback.BrowseFragment"; + private static final boolean TEST_ENTRANCE_TRANSITION = true; private static final int NUM_ROWS = 10; private ArrayObjectAdapter mRowsAdapter; @@ -62,6 +64,18 @@ public class BrowseFragment extends android.support.v17.leanback.app.BrowseFragm Log.i(TAG, "onItemSelected: " + item + " row " + row); } }); + if (TEST_ENTRANCE_TRANSITION) { + // don't run entrance transition if Activity is restored. + if (savedInstanceState == null) { + prepareEntranceTransition(); + } + // simulate delay loading data + new Handler().postDelayed(new Runnable() { + public void run() { + startEntranceTransition(); + } + }, 2000); + } } private void setupRows() { @@ -78,14 +92,14 @@ public class BrowseFragment extends android.support.v17.leanback.app.BrowseFragm for (int i = 0; i < NUM_ROWS; ++i) { ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter); listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1)); - listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_2)); - listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_3)); + listRowAdapter.add(new PhotoItem("This is a test", "Only a test", R.drawable.gallery_photo_2)); + listRowAdapter.add(new PhotoItem("Android TV", "by Google", R.drawable.gallery_photo_3)); listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_4)); listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_5)); - listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_6)); - listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_7)); - listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_8)); - HeaderItem header = new HeaderItem(i, "Row " + i, null); + listRowAdapter.add(new PhotoItem("This is a test", "Only a test", R.drawable.gallery_photo_6)); + listRowAdapter.add(new PhotoItem("Android TV", "open RowsActivity", R.drawable.gallery_photo_7)); + listRowAdapter.add(new PhotoItem("Leanback", "open MainActivity", R.drawable.gallery_photo_8)); + HeaderItem header = new HeaderItem(i, "Row " + i); mRowsAdapter.add(new ListRow(header, listRowAdapter)); } @@ -96,13 +110,25 @@ public class BrowseFragment extends android.support.v17.leanback.app.BrowseFragm @Override public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { - Intent intent = new Intent(getActivity(), DetailsActivity.class); - intent.putExtra(DetailsActivity.EXTRA_ITEM, (PhotoItem) item); - Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation( - getActivity(), - ((ImageCardView)itemViewHolder.view).getMainImageView(), - DetailsActivity.SHARED_ELEMENT_NAME).toBundle(); + Intent intent; + Bundle bundle; + if ( ((PhotoItem) item).getImageResourceId() == R.drawable.gallery_photo_8) { + intent = new Intent(getActivity(), MainActivity.class); + bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity()) + .toBundle(); + } else if ( ((PhotoItem) item).getImageResourceId() == R.drawable.gallery_photo_7) { + intent = new Intent(getActivity(), RowsActivity.class); + bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity()) + .toBundle(); + } else { + intent = new Intent(getActivity(), DetailsActivity.class); + intent.putExtra(DetailsActivity.EXTRA_ITEM, (PhotoItem) item); + bundle = ActivityOptionsCompat.makeSceneTransitionAnimation( + getActivity(), + ((ImageCardView)itemViewHolder.view).getMainImageView(), + DetailsActivity.SHARED_ELEMENT_NAME).toBundle(); + } getActivity().startActivity(intent, bundle); } } diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java index 4c5334288..f5e486b86 100644 --- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/CardPresenter.java @@ -17,17 +17,21 @@ import android.content.Context; import android.graphics.drawable.Drawable; import android.support.v17.leanback.widget.ImageCardView; import android.support.v17.leanback.widget.Presenter; +import android.text.TextUtils; import android.util.Log; import android.view.ViewGroup; import android.view.View.MeasureSpec; import android.view.ViewGroup.LayoutParams; import android.widget.TextView; +import java.util.Random; + public class CardPresenter extends Presenter { private static final String TAG = "CardPresenter"; private static final int IMAGE_HEIGHT_DP = 120; + private static Random sRand = new Random(); private static int sRowHeight = 0; private static int sExpandedRowHeight = 0; @@ -62,8 +66,17 @@ public class CardPresenter extends Presenter { ImageCardView v = new ImageCardView(parent.getContext()); v.setFocusable(true); v.setFocusableInTouchMode(true); - v.setMainImageAdjustViewBounds(true); - v.setMainImageDimensions(LayoutParams.WRAP_CONTENT, getRowHeight(parent.getContext())); + // Randomly makes image view crop as a square or just stretch to original + // aspect ratio. + if (sRand.nextBoolean()) { + v.setMainImageAdjustViewBounds(false); + v.setMainImageDimensions(getRowHeight(parent.getContext()), + getRowHeight(parent.getContext())); + } else { + v.setMainImageAdjustViewBounds(true); + v.setMainImageDimensions(LayoutParams.WRAP_CONTENT, + getRowHeight(parent.getContext())); + } return new ViewHolder(v); } @@ -75,6 +88,9 @@ public class CardPresenter extends Presenter { .getDrawable(photoItem.getImageResourceId()); ((ImageCardView) viewHolder.view).setMainImage(drawable); ((ImageCardView) viewHolder.view).setTitleText(photoItem.getTitle()); + if (!TextUtils.isEmpty(photoItem.getContent())) { + ((ImageCardView) viewHolder.view).setContentText(photoItem.getContent()); + } } @Override diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java index 5cf086793..bc88e2109 100644 --- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/DetailsFragment.java @@ -16,9 +16,8 @@ package com.example.android.leanback; import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; +import android.os.Handler; import android.support.v4.app.ActivityOptionsCompat; -import android.support.v4.view.ViewCompat; import android.support.v17.leanback.widget.Action; import android.support.v17.leanback.widget.ArrayObjectAdapter; import android.support.v17.leanback.widget.ClassPresenterSelector; @@ -35,13 +34,8 @@ import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.RowPresenter; import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; import android.widget.Toast; -import java.util.ArrayList; - public class DetailsFragment extends android.support.v17.leanback.app.DetailsFragment { private static final String TAG = "leanback.DetailsFragment"; private static final String ITEM = "item"; @@ -49,6 +43,17 @@ public class DetailsFragment extends android.support.v17.leanback.app.DetailsFra private static final int NUM_ROWS = 3; private ArrayObjectAdapter mRowsAdapter; private PhotoItem mPhotoItem; + final CardPresenter cardPresenter = new CardPresenter(); + + private static final int ACTION_BUY = 1; + private static final int ACTION_RENT = 2; + private static final int ACTION_PLAY = 3; + + private static final boolean TEST_SHARED_ELEMENT_TRANSITION = true; + private static final boolean TEST_ENTRANCE_TRANSITION = true; + + private static final long TIME_TO_LOAD_OVERVIEW_ROW_MS = 1000; + private static final long TIME_TO_LOAD_RELATED_ROWS_MS = 2000; @Override public void onCreate(Bundle savedInstanceState) { @@ -59,8 +64,24 @@ public class DetailsFragment extends android.support.v17.leanback.app.DetailsFra DetailsOverviewRowPresenter dorPresenter = new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter()); dorPresenter.setOnActionClickedListener(new OnActionClickedListener() { + @Override public void onActionClicked(Action action) { Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show(); + if (action.getId() == ACTION_BUY) { + DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle() + "(Owned)"); + dor.setImageDrawable(getResources().getDrawable(mPhotoItem.getImageResourceId())); + dor.addAction(new Action(ACTION_PLAY, "Play")); + mRowsAdapter.replace(0, dor); + } else if (action.getId() == ACTION_RENT) { + DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle() + "(Rented)"); + dor.setImageDrawable(getResources().getDrawable(mPhotoItem.getImageResourceId())); + dor.addAction(new Action(ACTION_PLAY, "Play")); + dor.addAction(new Action(ACTION_BUY, "Buy $9.99")); + mRowsAdapter.replace(0, dor); + } else if (action.getId() == ACTION_PLAY) { + Intent intent = new Intent(getActivity(), PlaybackOverlayActivity.class); + getActivity().startActivity(intent); + } } }); @@ -74,8 +95,6 @@ public class DetailsFragment extends android.support.v17.leanback.app.DetailsFra if (item != null) { setItem(item); } - dorPresenter.setSharedElementEnterTransition(getActivity(), - DetailsActivity.SHARED_ELEMENT_NAME); setOnItemViewClickedListener(new OnItemViewClickedListener() { @Override @@ -101,6 +120,17 @@ public class DetailsFragment extends android.support.v17.leanback.app.DetailsFra Log.i(TAG, "onItemSelected: " + item + " row " + row); } }); + + if (TEST_SHARED_ELEMENT_TRANSITION) { + dorPresenter.setSharedElementEnterTransition(getActivity(), + DetailsActivity.SHARED_ELEMENT_NAME); + } + if (TEST_ENTRANCE_TRANSITION) { + // don't run entrance transition if Activity is restored. + if (savedInstanceState == null) { + prepareEntranceTransition(); + } + } } @Override @@ -113,24 +143,34 @@ public class DetailsFragment extends android.support.v17.leanback.app.DetailsFra mPhotoItem = photoItem; mRowsAdapter.clear(); - Resources res = getActivity().getResources(); - DetailsOverviewRow dor = new DetailsOverviewRow("Details Overview"); - dor.setImageDrawable(res.getDrawable(photoItem.getImageResourceId())); - dor.addAction(new Action(1, "Buy $9.99")); - dor.addAction(new Action(2, "Rent", "$3.99", res.getDrawable(R.drawable.ic_action_a))); - mRowsAdapter.add(dor); - - final CardPresenter cardPresenter = new CardPresenter(); - for (int i = 0; i < NUM_ROWS; ++i) { - ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter); - listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1)); - listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_2)); - listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_3)); - listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_4)); - HeaderItem header = new HeaderItem(i, "Row " + i, null); - mRowsAdapter.add(new ListRow(header, listRowAdapter)); - } - + new Handler().postDelayed(new Runnable() { + public void run() { + Resources res = getActivity().getResources(); + DetailsOverviewRow dor = new DetailsOverviewRow(mPhotoItem.getTitle()); + dor.setImageDrawable(res.getDrawable(mPhotoItem.getImageResourceId())); + dor.addAction(new Action(ACTION_BUY, "Buy $9.99")); + dor.addAction(new Action(ACTION_RENT, "Rent", "$3.99", res.getDrawable(R.drawable.ic_action_a))); + mRowsAdapter.add(0, dor); + setSelectedPosition(0, false); + } + }, TIME_TO_LOAD_OVERVIEW_ROW_MS); + + new Handler().postDelayed(new Runnable() { + public void run() { + for (int i = 0; i < NUM_ROWS; ++i) { + ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter); + listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1)); + listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_2)); + listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_3)); + listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_4)); + HeaderItem header = new HeaderItem(i, "Row " + i); + mRowsAdapter.add(new ListRow(header, listRowAdapter)); + } + if (TEST_ENTRANCE_TRANSITION) { + startEntranceTransition(); + } + } + }, TIME_TO_LOAD_RELATED_ROWS_MS); setAdapter(mRowsAdapter); } diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/HorizontalGridTestActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/HorizontalGridTestActivity.java new file mode 100644 index 000000000..59155afdf --- /dev/null +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/HorizontalGridTestActivity.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2013 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. + */ + +package com.example.android.leanback; + +import android.support.v7.widget.RecyclerView; +import android.support.v17.leanback.widget.HorizontalGridView; +import android.support.v17.leanback.widget.OnChildSelectedListener; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.util.SparseArray; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnFocusChangeListener; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import java.io.File; + +public class HorizontalGridTestActivity extends Activity { + private static final String TAG = "HorizontalGridTestActivity"; + private static final boolean DEBUG = true; + private static final String SELECT_ACTION = "android.test.leanback.widget.SELECT"; + private static final int NUM_ITEMS = 100; + private static final boolean STAGGERED = true; + + private HorizontalGridView mHorizontalGridView; + private int mScrollState = RecyclerView.SCROLL_STATE_IDLE; + + private RecyclerView.OnScrollListener mScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + if (DEBUG) { + final String[] stateNames = { "IDLE", "DRAGGING", "SETTLING" }; + Log.v(TAG, "onScrollStateChanged " + + (newState < stateNames.length ? stateNames[newState] : newState)); + } + mScrollState = newState; + } + }; + + private View createView() { + View view = getLayoutInflater().inflate(R.layout.horizontal_grid, null, false); + mHorizontalGridView = (HorizontalGridView) view.findViewById(R.id.gridview); + + mHorizontalGridView.setWindowAlignment(HorizontalGridView.WINDOW_ALIGN_BOTH_EDGE); + mHorizontalGridView.setWindowAlignmentOffsetPercent(35); + mHorizontalGridView.setOnChildSelectedListener(new OnChildSelectedListener() { + @Override + public void onChildSelected(ViewGroup parent, View view, int position, long id) { + if (DEBUG) Log.d(TAG, "onChildSelected position=" + position + " id="+id); + } + }); + return view; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + if (DEBUG) Log.v(TAG, "onCreate"); + + RecyclerView.Adapter adapter = new MyAdapter(); + + View view = createView(); + + mHorizontalGridView.setAdapter(new MyAdapter()); + setContentView(view); + + mHorizontalGridView.setOnScrollListener(mScrollListener); + } + + @Override + protected void onNewIntent(Intent intent) { + if (DEBUG) Log.v(TAG, "onNewIntent " + intent); + if (intent.getAction().equals(SELECT_ACTION)) { + int position = intent.getIntExtra("SELECT_POSITION", -1); + if (position >= 0) { + mHorizontalGridView.setSelectedPosition(position); + } + } + super.onNewIntent(intent); + } + + private OnFocusChangeListener mItemFocusChangeListener = new OnFocusChangeListener() { + + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (hasFocus) { + v.setBackgroundColor(Color.YELLOW); + } else { + v.setBackgroundColor(Color.LTGRAY); + } + } + }; + + private OnClickListener mItemClickListener = new OnClickListener() { + @Override + public void onClick(View v) { + mHorizontalGridView.getAdapter().notifyDataSetChanged(); + } + }; + + class MyAdapter extends RecyclerView.Adapter { + + private int[] mItemLengths; + + MyAdapter() { + mItemLengths = new int[NUM_ITEMS]; + for (int i = 0; i < mItemLengths.length; i++) { + mItemLengths[i] = STAGGERED ? (int)(Math.random() * 180) + 180 : 240; + } + } + + @Override + public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + if (DEBUG) Log.v(TAG, "createViewHolder " + viewType); + TextView textView = new TextView(parent.getContext()); + textView.setTextColor(Color.BLACK); + textView.setFocusable(true); + textView.setFocusableInTouchMode(true); + textView.setOnFocusChangeListener(mItemFocusChangeListener); + textView.setOnClickListener(mItemClickListener); + return new ViewHolder(textView); + } + + @Override + public void onBindViewHolder(RecyclerView.ViewHolder baseHolder, int position) { + if (DEBUG) Log.v(TAG, "bindViewHolder " + position + " " + baseHolder); + ViewHolder holder = (ViewHolder) baseHolder; + ((TextView) holder.itemView).setText("Item "+position); + holder.itemView.setBackgroundColor(Color.LTGRAY); + holder.itemView.setLayoutParams(new ViewGroup.MarginLayoutParams(mItemLengths[position], + 80)); + } + + @Override + public int getItemCount() { + return mItemLengths.length; + } + } + + static class ViewHolder extends RecyclerView.ViewHolder { + + public ViewHolder(View v) { + super(v); + } + } +} diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PhotoItem.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PhotoItem.java index be3c8a6b1..adde7d350 100644 --- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PhotoItem.java +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PhotoItem.java @@ -19,10 +19,16 @@ import android.os.Parcelable; public class PhotoItem implements Parcelable { private String mTitle; + private String mContent; private int mImageResourceId; public PhotoItem(String title, int imageResourceId) { + this(title, null, imageResourceId); + } + + public PhotoItem(String title, String content, int imageResourceId) { mTitle = title; + mContent = content; mImageResourceId = imageResourceId; } @@ -34,6 +40,10 @@ public class PhotoItem implements Parcelable { return mTitle; } + public String getContent() { + return mContent; + } + @Override public String toString() { return mTitle; diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java index ed3d64f10..c37ca204e 100644 --- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/PlaybackOverlayFragment.java @@ -13,299 +13,395 @@ */ package com.example.android.leanback; -import java.util.ArrayList; - import android.content.Context; -import android.content.res.Resources; import android.graphics.drawable.Drawable; +import android.media.session.MediaController; +import android.media.session.MediaSessionManager; +import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.RemoteException; +import android.support.v17.leanback.app.MediaControllerGlue; +import android.support.v17.leanback.app.PlaybackControlGlue; import android.support.v17.leanback.widget.Action; import android.support.v17.leanback.widget.ArrayObjectAdapter; -import android.support.v17.leanback.widget.ClassPresenterSelector; -import android.support.v17.leanback.widget.AbstractDetailsDescriptionPresenter; import android.support.v17.leanback.widget.PlaybackControlsRow; -import android.support.v17.leanback.widget.PlaybackControlsRow.PlayPauseAction; import android.support.v17.leanback.widget.PlaybackControlsRow.RepeatAction; import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsUpAction; import android.support.v17.leanback.widget.PlaybackControlsRow.ThumbsDownAction; -import android.support.v17.leanback.widget.PlaybackControlsRow.ShuffleAction; -import android.support.v17.leanback.widget.PlaybackControlsRow.SkipNextAction; -import android.support.v17.leanback.widget.PlaybackControlsRow.SkipPreviousAction; import android.support.v17.leanback.widget.PlaybackControlsRowPresenter; import android.support.v17.leanback.widget.HeaderItem; -import android.support.v17.leanback.widget.VerticalGridView; +import android.support.v17.leanback.widget.PresenterSelector; import android.support.v17.leanback.widget.Row; import android.support.v17.leanback.widget.ListRow; import android.support.v17.leanback.widget.Presenter; import android.support.v17.leanback.widget.RowPresenter; import android.support.v17.leanback.widget.ListRowPresenter; -import android.support.v17.leanback.widget.OnActionClickedListener; import android.support.v17.leanback.widget.OnItemViewSelectedListener; import android.support.v17.leanback.widget.OnItemViewClickedListener; import android.support.v17.leanback.widget.ControlButtonPresenterSelector; +import android.support.v17.leanback.widget.SparseArrayObjectAdapter; +import android.support.v4.media.session.MediaControllerCompat; +import android.support.v4.media.session.MediaSessionCompat; import android.util.Log; import android.widget.Toast; +import java.util.List; + public class PlaybackOverlayFragment extends android.support.v17.leanback.app.PlaybackOverlayFragment { private static final String TAG = "leanback.PlaybackControlsFragment"; - private static final boolean SHOW_DETAIL = true; - private static final boolean HIDE_MORE_ACTIONS = false; - private static final int PRIMARY_CONTROLS = 5; - private static final boolean SHOW_IMAGE = PRIMARY_CONTROLS <= 5; + /** + * Change this to choose a different overlay background. + */ private static final int BACKGROUND_TYPE = PlaybackOverlayFragment.BG_LIGHT; - private static final int MORE_ROWS = 3; - private ArrayObjectAdapter mRowsAdapter; - private ArrayObjectAdapter mPrimaryActionsAdapter; - private ArrayObjectAdapter mSecondaryActionsAdapter; - private PlayPauseAction mPlayPauseAction; + /** + * Change the number of related content rows. + */ + private static final int RELATED_CONTENT_ROWS = 3; + + /** + * Change the location of the thumbs up/down controls + */ + private static final boolean THUMBS_PRIMARY = true; + + /** + * Change this to select hidden + */ + private static final boolean SECONDARY_HIDDEN = false; + + private static final String FAUX_TITLE = "A short song of silence"; + private static final String FAUX_SUBTITLE = "2014"; + private static final int FAUX_DURATION = 33 * 1000; + + private static final int ROW_CONTROLS = 0; + + private PlaybackControlGlue mGlue; + private PlaybackControlsRowPresenter mPlaybackControlsRowPresenter; + private ListRowPresenter mListRowPresenter; + private RepeatAction mRepeatAction; private ThumbsUpAction mThumbsUpAction; private ThumbsDownAction mThumbsDownAction; - private ShuffleAction mShuffleAction; - private SkipNextAction mSkipNextAction; - private SkipPreviousAction mSkipPreviousAction; - private PlaybackControlsRow mPlaybackControlsRow; - private ArrayList<MediaItem> mItems = new ArrayList<MediaItem>(); - private int mCurrentItem; private Handler mHandler; - private Runnable mRunnable; + + // These should match the playback service FF behavior + private int[] mFastForwardSpeeds = { 2, 3, 4, 5 }; + + private OnItemViewClickedListener mOnItemViewClickedListener = new OnItemViewClickedListener() { + @Override + public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, + RowPresenter.ViewHolder rowViewHolder, Row row) { + if (item instanceof Action) { + onActionClicked((Action) item); + } + } + }; + + private OnItemViewSelectedListener mOnItemViewSelectedListener = new OnItemViewSelectedListener() { + @Override + public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, + RowPresenter.ViewHolder rowViewHolder, Row row) { + Log.i(TAG, "onItemSelected: " + item + " row " + row); + } + }; + + final Runnable mUpdateProgressRunnable = new Runnable() { + @Override + public void run() { + mGlue.updateProgress(); + mHandler.postDelayed(this, mGlue.getUpdatePeriod()); + } + }; + + public SparseArrayObjectAdapter getAdapter() { + return (SparseArrayObjectAdapter) super.getAdapter(); + } @Override public void onCreate(Bundle savedInstanceState) { Log.i(TAG, "onCreate"); super.onCreate(savedInstanceState); + setBackgroundType(BACKGROUND_TYPE); + setOnItemViewSelectedListener(mOnItemViewSelectedListener); + + createComponents(getActivity()); + } + + private void createComponents(Context context) { mHandler = new Handler(); + mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context); + mThumbsUpAction.setIndex(ThumbsUpAction.OUTLINE); + mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context); + mThumbsDownAction.setIndex(ThumbsDownAction.OUTLINE); + mRepeatAction = new PlaybackControlsRow.RepeatAction(context); + + mGlue = new PlaybackControlGlue(context, this, mFastForwardSpeeds) { + private boolean mIsPlaying; + private int mSpeed = PlaybackControlGlue.PLAYBACK_SPEED_PAUSED; + private long mStartTime; + private long mStartPosition = 0; - setBackgroundType(BACKGROUND_TYPE); - setFadingEnabled(false); + @Override + protected SparseArrayObjectAdapter createPrimaryActionsAdapter( + PresenterSelector presenterSelector) { + return PlaybackOverlayFragment.this.createPrimaryActionsAdapter( + presenterSelector); + } - setupRows(); + @Override + public boolean hasValidMedia() { + return true; + } - setOnItemViewSelectedListener(new OnItemViewSelectedListener() { @Override - public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, - RowPresenter.ViewHolder rowViewHolder, Row row) { - Log.i(TAG, "onItemSelected: " + item + " row " + row); + public boolean isMediaPlaying() { + return mIsPlaying; } - }); - setOnItemViewClickedListener(new OnItemViewClickedListener() { + @Override - public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, - RowPresenter.ViewHolder rowViewHolder, Row row) { - Log.i(TAG, "onItemClicked: " + item + " row " + row); + public CharSequence getMediaTitle() { + return FAUX_TITLE; } - }); - } + @Override + public CharSequence getMediaSubtitle() { + return FAUX_SUBTITLE; + } + + @Override + public int getMediaDuration() { + return FAUX_DURATION; + } - private void setupRows() { - ClassPresenterSelector ps = new ClassPresenterSelector(); + @Override + public Drawable getMediaArt() { + return null; + } - PlaybackControlsRowPresenter playbackControlsRowPresenter; - if (SHOW_DETAIL) { - playbackControlsRowPresenter = new PlaybackControlsRowPresenter( - new DescriptionPresenter()); - } else { - playbackControlsRowPresenter = new PlaybackControlsRowPresenter(); - } - playbackControlsRowPresenter.setOnActionClickedListener(new OnActionClickedListener() { - public void onActionClicked(Action action) { - if (action.getId() == mPlayPauseAction.getId()) { - if (mPlayPauseAction.getIndex() == PlayPauseAction.PLAY) { - startProgressAutomation(); - setFadingEnabled(true); - } else { - stopProgressAutomation(); - setFadingEnabled(false); + @Override + public long getSupportedActions() { + return PlaybackControlGlue.ACTION_PLAY_PAUSE | + PlaybackControlGlue.ACTION_FAST_FORWARD | + PlaybackControlGlue.ACTION_REWIND; + } + + @Override + public int getCurrentSpeedId() { + return mSpeed; + } + + @Override + public int getCurrentPosition() { + int speed; + if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED) { + speed = 0; + } else if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_NORMAL) { + speed = 1; + } else if (mSpeed >= PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) { + int index = mSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0; + speed = getFastForwardSpeeds()[index]; + } else if (mSpeed <= -PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) { + int index = -mSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0; + speed = -getRewindSpeeds()[index]; + } else { + return -1; + } + long position = mStartPosition + + (System.currentTimeMillis() - mStartTime) * speed; + if (position > getMediaDuration()) { + position = getMediaDuration(); + onPlaybackComplete(true); + } else if (position < 0) { + position = 0; + onPlaybackComplete(false); + } + return (int) position; + } + + void onPlaybackComplete(final boolean ended) { + mHandler.post(new Runnable() { + @Override + public void run() { + if (mRepeatAction.getIndex() == RepeatAction.NONE) { + pausePlayback(); + } else { + startPlayback(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL); + } + mStartPosition = 0; + onStateChanged(); } - } else if (action.getId() == mSkipNextAction.getId()) { - next(); - } else if (action.getId() == mSkipPreviousAction.getId()) { - Toast.makeText(getActivity(), "TODO", Toast.LENGTH_SHORT).show(); + }); + } + + @Override + protected void startPlayback(int speed) { + if (speed == mSpeed) { + return; } - if (action instanceof PlaybackControlsRow.MultiAction) { - ((PlaybackControlsRow.MultiAction) action).nextIndex(); - notifyChanged(action); + mStartPosition = getCurrentPosition(); + mSpeed = speed; + mIsPlaying = true; + mStartTime = System.currentTimeMillis(); + } + + @Override + protected void pausePlayback() { + if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED) { + return; } + mStartPosition = getCurrentPosition(); + mSpeed = PlaybackControlGlue.PLAYBACK_SPEED_PAUSED; + mIsPlaying = false; } - }); - playbackControlsRowPresenter.setSecondaryActionsHidden(HIDE_MORE_ACTIONS); - ps.addClassPresenter(PlaybackControlsRow.class, playbackControlsRowPresenter); - ps.addClassPresenter(ListRow.class, new ListRowPresenter()); - mRowsAdapter = new ArrayObjectAdapter(ps); + @Override + protected void skipToNext() { + // Not supported + } - addPlaybackControlsRow(); - addOtherRows(); + @Override + protected void skipToPrevious() { + // Not supported + } - setAdapter(mRowsAdapter); - } + @Override + protected void onRowChanged(PlaybackControlsRow row) { + PlaybackOverlayFragment.this.onRowChanged(row); + } - private void addPlaybackControlsRow() { - Context context = getActivity(); + @Override + public void enableProgressUpdating(boolean enable) { + PlaybackOverlayFragment.this.enableProgressUpdating(enable); + } - if (SHOW_DETAIL) { - mPlaybackControlsRow = new PlaybackControlsRow(new MediaItem()); - } else { - mPlaybackControlsRow = new PlaybackControlsRow(); - } - mRowsAdapter.add(mPlaybackControlsRow); - - mItems = new ArrayList<MediaItem>(); - mItems.add(new MediaItem("Awesome Tune", "The More Awesome Band", R.drawable.details_img, 15*1000)); - mItems.add(new MediaItem("Pretty nice Tune", "The Nice Guys", R.drawable.details_img, 10*1000)); - mCurrentItem = 1; - updatePlaybackRow(mCurrentItem); - - ControlButtonPresenterSelector presenterSelector = new ControlButtonPresenterSelector(); - mPrimaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); - mSecondaryActionsAdapter = new ArrayObjectAdapter(presenterSelector); - mPlaybackControlsRow.setPrimaryActionsAdapter(mPrimaryActionsAdapter); - mPlaybackControlsRow.setSecondaryActionsAdapter(mSecondaryActionsAdapter); - - mPlayPauseAction = new PlayPauseAction(context); - mRepeatAction = new RepeatAction(context); - mThumbsUpAction = new ThumbsUpAction(context); - mThumbsDownAction = new ThumbsDownAction(context); - mShuffleAction = new ShuffleAction(context); - mSkipNextAction = new PlaybackControlsRow.SkipNextAction(context); - mSkipPreviousAction = new PlaybackControlsRow.SkipPreviousAction(context); - - if (PRIMARY_CONTROLS > 5) { - mPrimaryActionsAdapter.add(mThumbsUpAction); - } else { - mSecondaryActionsAdapter.add(mThumbsUpAction); - } - mPrimaryActionsAdapter.add(mSkipPreviousAction); - if (PRIMARY_CONTROLS > 3) { - mPrimaryActionsAdapter.add(new PlaybackControlsRow.RewindAction(context)); + @Override + public int getUpdatePeriod() { + return PlaybackOverlayFragment.this.getUpdatePeriod(); + } + }; + + mGlue.setOnItemViewClickedListener(mOnItemViewClickedListener); + + mPlaybackControlsRowPresenter = mGlue.createControlsRowAndPresenter(); + mPlaybackControlsRowPresenter.setSecondaryActionsHidden(SECONDARY_HIDDEN); + mListRowPresenter = new ListRowPresenter(); + + setAdapter(new SparseArrayObjectAdapter(new PresenterSelector() { + @Override + public Presenter getPresenter(Object object) { + if (object instanceof PlaybackControlsRow) { + return mPlaybackControlsRowPresenter; + } else if (object instanceof ListRow) { + return mListRowPresenter; + } + throw new IllegalArgumentException("Unhandled object: " + object); + } + })); + + // Set secondary control actions + PlaybackControlsRow controlsRow = mGlue.getControlsRow(); + ArrayObjectAdapter adapter = new ArrayObjectAdapter(new ControlButtonPresenterSelector()); + controlsRow.setSecondaryActionsAdapter(adapter); + if (!THUMBS_PRIMARY) { + adapter.add(mThumbsDownAction); } - mPrimaryActionsAdapter.add(mPlayPauseAction); - if (PRIMARY_CONTROLS > 3) { - mPrimaryActionsAdapter.add(new PlaybackControlsRow.FastForwardAction(context)); + adapter.add(mRepeatAction); + if (!THUMBS_PRIMARY) { + adapter.add(mThumbsUpAction); } - mPrimaryActionsAdapter.add(mSkipNextAction); - mSecondaryActionsAdapter.add(mRepeatAction); - mSecondaryActionsAdapter.add(mShuffleAction); - if (PRIMARY_CONTROLS > 5) { - mPrimaryActionsAdapter.add(mThumbsDownAction); - } else { - mSecondaryActionsAdapter.add(mThumbsDownAction); + // Add the controls row + getAdapter().set(ROW_CONTROLS, controlsRow); + + // Add related content rows + for (int i = 0; i < RELATED_CONTENT_ROWS; ++i) { + ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new StringPresenter()); + listRowAdapter.add("Some related content"); + listRowAdapter.add("Other related content"); + HeaderItem header = new HeaderItem(i, "Row " + i); + getAdapter().set(ROW_CONTROLS + 1 + i, new ListRow(header, listRowAdapter)); } - mSecondaryActionsAdapter.add(new PlaybackControlsRow.HighQualityAction(context)); - mSecondaryActionsAdapter.add(new PlaybackControlsRow.ClosedCaptioningAction(context)); } - private void notifyChanged(Action action) { - ArrayObjectAdapter adapter = mPrimaryActionsAdapter; - if (adapter.indexOf(action) >= 0) { - adapter.notifyArrayItemRangeChanged(adapter.indexOf(action), 1); - return; - } - adapter = mSecondaryActionsAdapter; - if (adapter.indexOf(action) >= 0) { - adapter.notifyArrayItemRangeChanged(adapter.indexOf(action), 1); - return; + private SparseArrayObjectAdapter createPrimaryActionsAdapter( + PresenterSelector presenterSelector) { + SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(presenterSelector); + if (THUMBS_PRIMARY) { + adapter.set(PlaybackControlGlue.ACTION_CUSTOM_LEFT_FIRST, mThumbsUpAction); + adapter.set(PlaybackControlGlue.ACTION_CUSTOM_RIGHT_FIRST, mThumbsDownAction); } + return adapter; } - private void updatePlaybackRow(int index) { - if (mPlaybackControlsRow.getItem() != null) { - MediaItem item = (MediaItem) mPlaybackControlsRow.getItem(); - item.title = mItems.get(index).title; - item.subtitle = mItems.get(index).subtitle; + private void onRowChanged(PlaybackControlsRow row) { + if (getAdapter() == null) { + return; } - if (SHOW_IMAGE) { - mPlaybackControlsRow.setImageDrawable(getResources().getDrawable( - mItems.get(mCurrentItem).imageResId)); + int index = getAdapter().indexOf(row); + if (index >= 0) { + getAdapter().notifyArrayItemRangeChanged(index, 1); } - mRowsAdapter.notifyArrayItemRangeChanged(0, 1); - - mPlaybackControlsRow.setTotalTime(mItems.get(mCurrentItem).durationMs); - mPlaybackControlsRow.setCurrentTime(0); - mPlaybackControlsRow.setBufferedProgress(75 * 1000); } - private void addOtherRows() { - for (int i = 0; i < MORE_ROWS; ++i) { - ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new StringPresenter()); - listRowAdapter.add("Some related content"); - listRowAdapter.add("Other related content"); - HeaderItem header = new HeaderItem(i, "Row " + i, null); - mRowsAdapter.add(new ListRow(header, listRowAdapter)); + private void enableProgressUpdating(boolean enable) { + Log.v(TAG, "enableProgressUpdating " + enable + " this " + this); + mHandler.removeCallbacks(mUpdateProgressRunnable); + if (enable) { + mUpdateProgressRunnable.run(); } } private int getUpdatePeriod() { - if (getView() == null || mPlaybackControlsRow.getTotalTime() <= 0) { + int totalTime = mGlue.getControlsRow().getTotalTime(); + if (getView() == null || totalTime <= 0) { return 1000; } - return Math.max(16, mPlaybackControlsRow.getTotalTime() / getView().getWidth()); + return Math.max(16, totalTime / getView().getWidth()); } - private void startProgressAutomation() { - mRunnable = new Runnable() { - @Override - public void run() { - int updatePeriod = getUpdatePeriod(); - int currentTime = mPlaybackControlsRow.getCurrentTime() + updatePeriod; - int totalTime = mPlaybackControlsRow.getTotalTime(); - mPlaybackControlsRow.setCurrentTime(currentTime); - if (totalTime > 0 && totalTime <= currentTime) { - next(); - } - mHandler.postDelayed(this, updatePeriod); - } - }; - mHandler.postDelayed(mRunnable, getUpdatePeriod()); - } - - private void next() { - if (++mCurrentItem >= mItems.size()) { - mCurrentItem = 0; + private void onActionClicked(Action action) { + Log.v(TAG, "onActionClicked " + action); + Toast.makeText(getActivity(), action.toString(), Toast.LENGTH_SHORT).show(); + if (action instanceof PlaybackControlsRow.MultiAction) { + PlaybackControlsRow.MultiAction multiAction = (PlaybackControlsRow.MultiAction) action; + multiAction.nextIndex(); + notifyActionChanged(multiAction); } - updatePlaybackRow(mCurrentItem); } - private void stopProgressAutomation() { - if (mHandler != null && mRunnable != null) { - mHandler.removeCallbacks(mRunnable); - } + private SparseArrayObjectAdapter getPrimaryActionsAdapter() { + return (SparseArrayObjectAdapter) mGlue.getControlsRow().getPrimaryActionsAdapter(); } - @Override - public void onStop() { - stopProgressAutomation(); - super.onStop(); + private ArrayObjectAdapter getSecondaryActionsAdapter() { + return (ArrayObjectAdapter) mGlue.getControlsRow().getSecondaryActionsAdapter(); } - static class MediaItem { - String title; - String subtitle; - int imageResId; - int durationMs; - - MediaItem() { + private void notifyActionChanged(PlaybackControlsRow.MultiAction action) { + int index; + index = getPrimaryActionsAdapter().indexOf(action); + if (index >= 0) { + getPrimaryActionsAdapter().notifyArrayItemRangeChanged(index, 1); + } else { + index = getSecondaryActionsAdapter().indexOf(action); + if (index >= 0) { + getSecondaryActionsAdapter().notifyArrayItemRangeChanged(index, 1); + } } + } - MediaItem(String title, String subtitle, int imageResId, int durationMs) { - this.title = title; - this.subtitle = subtitle; - this.imageResId = imageResId; - this.durationMs = durationMs; - } + @Override + public void onStart() { + super.onStart(); + mGlue.enableProgressUpdating(mGlue.hasValidMedia() && mGlue.isMediaPlaying()); } - static class DescriptionPresenter extends AbstractDetailsDescriptionPresenter { - @Override - protected void onBindDescription(ViewHolder vh, Object item) { - vh.getTitle().setText(((MediaItem) item).title); - vh.getSubtitle().setText(((MediaItem) item).subtitle); - } + @Override + public void onStop() { + mGlue.enableProgressUpdating(false); + super.onStop(); } } diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java new file mode 100644 index 000000000..133e99506 --- /dev/null +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsActivity.java @@ -0,0 +1,40 @@ +/* + * 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. + */ +package com.example.android.leanback; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v17.leanback.R; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +public class RowsActivity extends Activity implements RowsFragment.OnRowsFirstLineSelectedListener +{ + TextView mTitleView; + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + setContentView(R.layout.rows); + mTitleView = (TextView) findViewById(R.id.rows_title); + } + + @Override + public void onSelectedFirstRow(boolean firstRow) { + mTitleView.setVisibility(firstRow ? View.VISIBLE : View.INVISIBLE); + } +} diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsFragment.java new file mode 100644 index 000000000..2509bfc01 --- /dev/null +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/RowsFragment.java @@ -0,0 +1,124 @@ +/* + * 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. + */ +package com.example.android.leanback; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.ActivityOptionsCompat; +import android.support.v17.leanback.R; +import android.support.v17.leanback.widget.ArrayObjectAdapter; +import android.support.v17.leanback.widget.HeaderItem; +import android.support.v17.leanback.widget.ImageCardView; +import android.support.v17.leanback.widget.ListRow; +import android.support.v17.leanback.widget.ListRowPresenter; +import android.support.v17.leanback.widget.OnItemViewClickedListener; +import android.support.v17.leanback.widget.OnItemViewSelectedListener; +import android.support.v17.leanback.widget.Presenter; +import android.support.v17.leanback.widget.Row; +import android.support.v17.leanback.widget.RowPresenter; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +public class RowsFragment extends android.support.v17.leanback.app.RowsFragment { + + public static interface OnRowsFirstLineSelectedListener { + void onSelectedFirstRow(boolean firstRow); + } + + private static final String TAG = "leanback.RowsFragment"; + + private static final int NUM_ROWS = 10; + private ArrayObjectAdapter mRowsAdapter; + private OnRowsFirstLineSelectedListener mCallback; + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i(TAG, "onCreate"); + super.onCreate(savedInstanceState); + + setupRows(); + setOnItemViewClickedListener(new ItemViewClickedListener()); + setOnItemViewSelectedListener(new OnItemViewSelectedListener() { + @Override + public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item, + RowPresenter.ViewHolder rowViewHolder, Row row) { + Log.i(TAG, "onItemSelected: " + item + " row " + row); + if (mCallback == null) { + return; + } + if (mRowsAdapter != null && mRowsAdapter.size() > 0 && row != null && + row != mRowsAdapter.get(0)) { + mCallback.onSelectedFirstRow(false); + } else { + mCallback.onSelectedFirstRow(true); + } + } + }); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + // This makes sure that the container activity has implemented + if (activity instanceof OnRowsFirstLineSelectedListener) { + mCallback = (OnRowsFirstLineSelectedListener) activity; + } + } + + private void setupRows() { + ListRowPresenter lrp = new ListRowPresenter(); + lrp.setRowHeight(CardPresenter.getRowHeight(getActivity())); + lrp.setExpandedRowHeight(CardPresenter.getExpandedRowHeight(getActivity())); + + mRowsAdapter = new ArrayObjectAdapter(lrp); + + // For good performance, it's important to use a single instance of + // a card presenter for all rows using that presenter. + final CardPresenter cardPresenter = new CardPresenter(); + + for (int i = 0; i < NUM_ROWS; ++i) { + ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter); + listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1)); + listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_2)); + listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_3)); + listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_4)); + listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_5)); + listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_6)); + listRowAdapter.add(new PhotoItem("Android TV", R.drawable.gallery_photo_7)); + listRowAdapter.add(new PhotoItem("Leanback", R.drawable.gallery_photo_8)); + HeaderItem header = new HeaderItem(i, "Row " + i); + mRowsAdapter.add(new ListRow(header, listRowAdapter)); + } + + setAdapter(mRowsAdapter); + } + + private final class ItemViewClickedListener implements OnItemViewClickedListener { + @Override + public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, + RowPresenter.ViewHolder rowViewHolder, Row row) { + Intent intent = new Intent(getActivity(), DetailsActivity.class); + intent.putExtra(DetailsActivity.EXTRA_ITEM, (PhotoItem) item); + + Bundle bundle = ActivityOptionsCompat.makeSceneTransitionAnimation( + getActivity(), + ((ImageCardView)itemViewHolder.view).getMainImageView(), + DetailsActivity.SHARED_ELEMENT_NAME).toBundle(); + getActivity().startActivity(intent, bundle); + } + } +} diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchFragment.java index c271bc114..b55b82ffe 100644 --- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchFragment.java +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/SearchFragment.java @@ -73,7 +73,7 @@ public class SearchFragment extends android.support.v17.leanback.app.SearchFragm ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(new CardPresenter()); listRowAdapter.add(new PhotoItem("Hello world", R.drawable.gallery_photo_1)); listRowAdapter.add(new PhotoItem("This is a test", R.drawable.gallery_photo_2)); - HeaderItem header = new HeaderItem(i, mQuery + " results row " + i, null); + HeaderItem header = new HeaderItem(i, mQuery + " results row " + i); mRowsAdapter.add(new ListRow(header, listRowAdapter)); } } diff --git a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java index ae41b119c..9ffaf66f5 100644 --- a/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java +++ b/samples/SupportLeanbackDemos/src/com/example/android/leanback/VerticalGridFragment.java @@ -35,7 +35,15 @@ public class VerticalGridFragment extends android.support.v17.leanback.app.Verti private static final int NUM_ITEMS = 50; private static final int HEIGHT = 200; - private ArrayObjectAdapter mAdapter; + private static class Adapter extends ArrayObjectAdapter { + public Adapter(StringPresenter presenter) { + super(presenter); + } + public void callNotifyChanged() { + super.notifyChanged(); + } + } + private Adapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { @@ -53,7 +61,7 @@ public class VerticalGridFragment extends android.support.v17.leanback.app.Verti gridPresenter.setNumberOfColumns(NUM_COLUMNS); setGridPresenter(gridPresenter); - mAdapter = new ArrayObjectAdapter(new StringPresenter()); + mAdapter = new Adapter(new StringPresenter()); for (int i = 0; i < NUM_ITEMS; i++) { mAdapter.add(Integer.toString(i)); } @@ -72,6 +80,7 @@ public class VerticalGridFragment extends android.support.v17.leanback.app.Verti public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item, RowPresenter.ViewHolder rowViewHolder, Row row) { Log.i(TAG, "onItemClicked: " + item + " row " + row); + mAdapter.callNotifyChanged(); } }); setOnSearchClickedListener(new View.OnClickListener() { diff --git a/sdk/platform_source.prop_template b/sdk/platform_source.prop_template index c46f13d17..ccaa59727 100644 --- a/sdk/platform_source.prop_template +++ b/sdk/platform_source.prop_template @@ -2,9 +2,9 @@ Pkg.Desc=Android SDK Platform ${PLATFORM_VERSION} Pkg.UserSrc=false Platform.Version=${PLATFORM_VERSION} Platform.CodeName=LOLLIPOP -Pkg.Revision=3 +Pkg.Revision=1 AndroidVersion.ApiLevel=${PLATFORM_SDK_VERSION} AndroidVersion.CodeName=${PLATFORM_VERSION_CODENAME} Layoutlib.Api=13 -Layoutlib.Revision=3 +Layoutlib.Revision=1 Platform.MinToolsRev=22 diff --git a/sdk/sdk_files_NOTICE.txt b/sdk/sdk_files_NOTICE.txt index bb72a3464..2b3d8b57c 100644 --- a/sdk/sdk_files_NOTICE.txt +++ b/sdk/sdk_files_NOTICE.txt @@ -9740,6 +9740,7 @@ Notices for file(s): /bin/dexdump /bin/dx /bin/jasmin +/bin/split-select /framework/android-mock-generatorlib.jar /framework/jasmin.jar /lib/lib64cutils.a diff --git a/tools/idegen/src/Configuration.java b/tools/idegen/src/Configuration.java index 2f800b11a..c09be1a40 100644 --- a/tools/idegen/src/Configuration.java +++ b/tools/idegen/src/Configuration.java @@ -132,7 +132,7 @@ public class Configuration { String path = file.getPath().substring(2); // Keep track of source roots for .java files. - if (path.endsWith(".java")) { + if (path.endsWith(".java") && !file.isDirectory()) { if (firstJavaFile) { // Only parse one .java file per directory. firstJavaFile = false; |
