Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TIMOB-16436] Integrate appcompat themes to SDK #5348

Merged
merged 8 commits into from
Feb 20, 2014
6 changes: 3 additions & 3 deletions android/cli/commands/_build.js
Original file line number Diff line number Diff line change
Expand Up @@ -3277,7 +3277,7 @@ AndroidBuilder.prototype.generateTheme = function generateTheme(next) {
if (!fs.existsSync(themeFile)) {
this.logger.info(__('Generating %s', themeFile.cyan));

var flags = 'Theme';
var flags = 'Theme.AppCompat';
if ((this.tiapp.fullscreen || this.tiapp['statusbar-hidden']) && this.tiapp['navbar-hidden']) {
flags += '.NoTitleBar.Fullscreen';
} else if (this.tiapp['navbar-hidden']) {
Expand Down Expand Up @@ -3362,15 +3362,15 @@ AndroidBuilder.prototype.generateAndroidManifest = function generateAndroidManif
'activity': {
'name': 'ti.modules.titanium.media.TiVideoActivity',
'configChanges': ['keyboardHidden', 'orientation'],
'theme': '@android:style/Theme.NoTitleBar.Fullscreen',
'theme': '@style/Theme.AppCompat.NoTitleBar.Fullscreen',
'launchMode': 'singleTask'
}
},
'Media.showCamera': {
'activity': {
'name': 'ti.modules.titanium.media.TiCameraActivity',
'configChanges': ['keyboardHidden', 'orientation'],
'theme': '@android:style/Theme.Translucent.NoTitleBar.Fullscreen'
'theme': '@style/Theme.AppCompat.Translucent.NoTitleBar.Fullscreen'
}
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.app.ActionBar;
import android.support.v7.app.ActionBar;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
import ti.modules.titanium.ui.widget.tabgroup.TiUITabHostGroup;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;

@Kroll.proxy(creatableInModule=UIModule.class, propertyAccessors={
TiC.PROPERTY_TABS_BACKGROUND_COLOR,
Expand All @@ -52,7 +52,7 @@ public class TabGroupProxy extends TiWindowProxy implements TiActivityWindow
protected static final int MSG_LAST_ID = MSG_FIRST_ID + 999;

private ArrayList<TabProxy> tabs = new ArrayList<TabProxy>();
private WeakReference<Activity> tabGroupActivity;
private WeakReference<ActionBarActivity> tabGroupActivity;
private TabProxy selectedTab;
private boolean isFocused;

Expand Down Expand Up @@ -287,29 +287,31 @@ private TabProxy handleGetActiveTab() {
@Override
protected void handleOpen(KrollDict options)
{
Activity topActivity = TiApplication.getAppCurrentActivity();
Intent intent = new Intent(topActivity, TiActivity.class);
fillIntent(topActivity, intent);
Activity activity = TiApplication.getAppCurrentActivity();
if (activity instanceof ActionBarActivity) {
ActionBarActivity topActivity = (ActionBarActivity) TiApplication.getAppCurrentActivity();
Intent intent = new Intent(topActivity, TiActivity.class);
fillIntent(topActivity, intent);

int windowId = TiActivityWindows.addWindow(this);
intent.putExtra(TiC.INTENT_PROPERTY_USE_ACTIVITY_WINDOW, true);
intent.putExtra(TiC.INTENT_PROPERTY_WINDOW_ID, windowId);
int windowId = TiActivityWindows.addWindow(this);
intent.putExtra(TiC.INTENT_PROPERTY_USE_ACTIVITY_WINDOW, true);
intent.putExtra(TiC.INTENT_PROPERTY_WINDOW_ID, windowId);

topActivity.startActivity(intent);
topActivity.startActivity(intent);
} else {
Log.e(TAG, "TabGroupProxy requires an activity that is a descendent of ActionBarActivity to work with AppCompat", Log.DEBUG_MODE);
}
}

@Override
public void windowCreated(TiBaseActivity activity) {
tabGroupActivity = new WeakReference<Activity>(activity);
tabGroupActivity = new WeakReference<ActionBarActivity>(activity);
activity.setWindowProxy(this);
activity.setLayoutProxy(this);
setActivity(activity);

// Use the navigation tabs if this platform supports the action bar.
// Otherwise we will fall back to using the TabHost implementation.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && activity.getActionBar() != null) {
if (activity.getSupportActionBar() != null) {
view = new TiUIActionBarTabGroup(this, activity);

} else {
view = new TiUITabHostGroup(this, activity);
}
Expand Down Expand Up @@ -371,7 +373,7 @@ protected void handleClose(KrollDict options)
releaseViews();
view = null;

Activity activity = tabGroupActivity.get();
ActionBarActivity activity = tabGroupActivity.get();
if (activity != null && !activity.isFinishing()) {
activity.finish();
}
Expand Down Expand Up @@ -446,7 +448,7 @@ public void onTabSelected(TabProxy tabProxy) {
selectedTab.onFocusChanged(true, focusEventData);
}

private void fillIntent(Activity activity, Intent intent)
private void fillIntent(ActionBarActivity activity, Intent intent)
{
if (hasProperty(TiC.PROPERTY_FULLSCREEN)) {
intent.putExtra(TiC.PROPERTY_FULLSCREEN, TiConvert.toBoolean(getProperty(TiC.PROPERTY_FULLSCREEN)));
Expand Down Expand Up @@ -502,7 +504,7 @@ public void releaseViewsForActivityForcedToDestroy()
}

@Override
protected Activity getWindowActivity()
protected ActionBarActivity getWindowActivity()
{
return (tabGroupActivity != null) ? tabGroupActivity.get() : null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@
package ti.modules.titanium.ui.widget.tabgroup;

import org.appcelerator.kroll.KrollProxy;
import org.appcelerator.kroll.common.Log;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.util.TiUIHelper;

import ti.modules.titanium.ui.TabProxy;
import android.app.ActionBar;
import android.app.Fragment;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.LayoutInflater;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@

import ti.modules.titanium.ui.TabGroupProxy;
import ti.modules.titanium.ui.TabProxy;
import android.app.ActionBar;
import android.app.ActionBar.Tab;
import android.app.ActionBar.TabListener;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBar.Tab;
import android.support.v7.app.ActionBar.TabListener;
import android.app.Activity;
import android.app.FragmentTransaction;
import android.support.v4.app.FragmentTransaction;
import android.view.ViewGroup;
import android.widget.FrameLayout;

Expand Down Expand Up @@ -50,7 +50,7 @@ public TiUIActionBarTabGroup(TabGroupProxy proxy, TiBaseActivity activity) {
activity.addOnLifecycleEventListener(this);

// Setup the action bar for navigation tabs.
actionBar = activity.getActionBar();
actionBar = activity.getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
actionBar.setDisplayShowTitleEnabled(true);

Expand Down Expand Up @@ -130,7 +130,16 @@ public void selectTab(TabProxy tabProxy) {

@Override
public TabProxy getSelectedTab() {
ActionBar.Tab tab = actionBar.getSelectedTab();
ActionBar.Tab tab;
try {
tab = actionBar.getSelectedTab();
} catch (NullPointerException e) {
//This is a workaround for AppCompat actionbar 4.0+. There is a bug in AppCompat source that
//will cause a null pointer exception if no tab is selected instead of returning null. See source at:
//https://android.googlesource.com/platform/frameworks/support/+/89208232f3b5d1451408d787872504a190bc7ee0/v7/appcompat/src/android/support/v7/app/ActionBarImplICS.java
//line 259.
tab = null;
}
if (tab == null) {
// There is no selected tab currently for this action bar.
// This probably means the tab group contains no tabs.
Expand Down
5 changes: 3 additions & 2 deletions android/templates/build/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

<application android:icon="@drawable/appicon"
android:label="<%- tiapp.name %>" android:name="<%- classname %>Application"
android:debuggable="false">
android:debuggable="false"
android:theme="@style/Theme.AppCompat">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of applying theme to the application I would instead apply theme to TiActivity.
That way any modules that have their own activities that do not extend TiBaseActivity will continue to work as before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

theme to application includes all sub activities that don't have a theme set, so TiActivity will be using the theme set for application as well... It's a good default to have, i.e even new activities from modules will use the application theme as default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we decide to apply the theme to TiActivity, we will also need to do it for activities like JSActivity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested and confirmed that AppCompat theme works with non-actionbar activities (i.e activites that do not extend TiBaseActivity), so we should be fine using it as a default theme.


<activity android:name=".<%- classname %>Activity"
android:label="@string/app_name" android:theme="@style/Theme.Titanium"
Expand All @@ -28,7 +29,7 @@
android:configChanges="keyboardHidden|orientation" />
<activity android:name="org.appcelerator.titanium.TiTranslucentActivity"
android:configChanges="keyboardHidden|orientation"
android:theme="@android:style/Theme.Translucent" />
android:theme="@style/Theme.AppCompat.Translucent" />
<activity android:name="ti.modules.titanium.ui.android.TiPreferencesActivity" />

</application>
Expand Down
30 changes: 29 additions & 1 deletion android/templates/build/theme.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.Titanium" parent="android:<%- flags %>">
<style name="Theme.AppCompat.NoTitleBar">
<item name="android:windowNoTitle">true</item>
</style>

<style name="Theme.AppCompat.NoTitleBar.Fullscreen">
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>

<style name="Theme.Titanium" parent="@style/<%- flags %>">
<item name="android:windowBackground">@drawable/background</item>
<item name="android:windowActionBar">false</item>
</style>

<style name="Theme.AppCompat.Translucent">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:actionBarStyle">@style/Widget.AppCompat.Base.ActionBar.Solid</item>
<!-- AppCompat Compatibility -->
<item name="actionBarStyle">@style/Widget.AppCompat.Base.ActionBar.Solid</item>
</style>

<style name="Theme.AppCompat.Translucent.NoTitleBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>

<style name="Theme.AppCompat.Translucent.NoTitleBar.Fullscreen">
<item name="android:windowFullscreen">true</item>
</style>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.ActionBarActivity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
Expand All @@ -61,7 +62,7 @@
* The base class for all non tab Titanium activities. To learn more about Activities, see the
* <a href="http://developer.android.com/reference/android/app/Activity.html">Android Activity documentation</a>.
*/
public abstract class TiBaseActivity extends FragmentActivity
public abstract class TiBaseActivity extends ActionBarActivity
implements TiActivitySupport/*, ITiWindowHandler*/
{
private static final String TAG = "TiBaseActivity";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
import org.appcelerator.titanium.util.TiFileHelper;
import org.appcelerator.titanium.util.TiUrl;

import android.app.ActionBar;
import android.app.Activity;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Message;
Expand Down Expand Up @@ -46,10 +46,10 @@ public class ActionBarProxy extends KrollProxy

private ActionBar actionBar;

public ActionBarProxy(Activity activity)
public ActionBarProxy(ActionBarActivity activity)
{
super();
actionBar = activity.getActionBar();
actionBar = activity.getSupportActionBar();
}

@Kroll.method @Kroll.setProperty
Expand Down Expand Up @@ -164,29 +164,27 @@ public void hide()
@Kroll.method @Kroll.setProperty
public void setLogo(String url)
{
if (Build.VERSION.SDK_INT >= TiC.API_LEVEL_ICE_CREAM_SANDWICH) {
if (TiApplication.isUIThread()) {
handleSetLogo(url);
} else {
Message message = getMainHandler().obtainMessage(MSG_SET_LOGO, url);
message.getData().putString(LOGO, url);
message.sendToTarget();
}
if (TiApplication.isUIThread()) {
handleSetLogo(url);
} else {
Message message = getMainHandler().obtainMessage(MSG_SET_LOGO, url);
message.getData().putString(LOGO, url);
message.sendToTarget();
}

}

@Kroll.method @Kroll.setProperty
public void setIcon(String url)
{
if (Build.VERSION.SDK_INT >= TiC.API_LEVEL_ICE_CREAM_SANDWICH) {
if (TiApplication.isUIThread()) {
handleSetIcon(url);
} else {
Message message = getMainHandler().obtainMessage(MSG_SET_ICON, url);
message.getData().putString(ICON, url);
message.sendToTarget();
}
if (TiApplication.isUIThread()) {
handleSetIcon(url);
} else {
Message message = getMainHandler().obtainMessage(MSG_SET_ICON, url);
message.getData().putString(ICON, url);
message.sendToTarget();
}

}

private void handleSetIcon(String url)
Expand Down Expand Up @@ -331,8 +329,7 @@ public boolean handleMessage(Message msg)
@Override
public void onPropertyChanged(String name, Object value)
{
if (Build.VERSION.SDK_INT >= TiC.API_LEVEL_ICE_CREAM_SANDWICH
&& TiC.PROPERTY_ON_HOME_ICON_ITEM_SELECTED.equals(name)) {
if (TiC.PROPERTY_ON_HOME_ICON_ITEM_SELECTED.equals(name)) {
// If we have a listener on the home icon item, then enable the home button (we need to do this for ICS and
// above)
if (TiApplication.isUIThread()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.appcelerator.titanium.util.TiActivitySupport;
import org.appcelerator.titanium.util.TiActivitySupportHelper;

import android.support.v7.app.ActionBarActivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
Expand Down Expand Up @@ -245,11 +246,10 @@ public TiWindowProxy getWindow()
@Kroll.method @Kroll.getProperty
public ActionBarProxy getActionBar()
{
Activity activity = getWrappedActivity();
if (actionBarProxy == null && activity != null && Build.VERSION.SDK_INT >= TiC.API_LEVEL_HONEYCOMB) {
ActionBarActivity activity = (ActionBarActivity) getWrappedActivity();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to cast this one to an ActionBarActivity ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, getWrappedActivity() returns type Activity. We only cast to ActionBarActivity when an action bar is involved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh also because ActionBarProxy constructor requires type ActionBarActivity

if (actionBarProxy == null && activity != null) {
actionBarProxy = new ActionBarProxy(activity);
}

return actionBarProxy;
}

Expand All @@ -266,12 +266,10 @@ public void openOptionsMenu()
@Kroll.method
public void invalidateOptionsMenu()
{
if (Build.VERSION.SDK_INT >= TiC.API_LEVEL_HONEYCOMB) {
if (TiApplication.isUIThread()) {
if (TiApplication.isUIThread()) {
handleInvalidateOptionsMenu();
} else {
} else {
getMainHandler().obtainMessage(MSG_INVALIDATE_OPTIONS_MENU).sendToTarget();
}
}
}

Expand All @@ -286,8 +284,8 @@ private void handleOpenOptionsMenu()
private void handleInvalidateOptionsMenu()
{
Activity activity = getWrappedActivity();
if (activity != null) {
activity.invalidateOptionsMenu();
if (activity != null && activity instanceof ActionBarActivity) {
((ActionBarActivity)activity).supportInvalidateOptionsMenu();
}
}

Expand Down