/* * Copyright (C) 2011 The original author or authors. * * 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.zapta.apps.maniana.view; import static com.zapta.apps.maniana.util.Assertions.check; import static com.zapta.apps.maniana.util.Assertions.checkNotNull; import javax.annotation.Nullable; import android.annotation.SuppressLint; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.TextView; import com.zapta.apps.maniana.R; import com.zapta.apps.maniana.annotations.MainActivityScope; import com.zapta.apps.maniana.main.MainActivityState; import com.zapta.apps.maniana.menus.ItemMenuEntry; import com.zapta.apps.maniana.menus.MainMenu; import com.zapta.apps.maniana.menus.MainMenuEntry; import com.zapta.apps.maniana.model.PageKind; import com.zapta.apps.maniana.services.MainActivityServices; import com.zapta.apps.maniana.settings.Font; import com.zapta.apps.maniana.settings.TypefaceSpec; import com.zapta.apps.maniana.settings.PageIconSet; import com.zapta.apps.maniana.util.ColorUtil; import com.zapta.apps.maniana.util.DisplayUtil; import com.zapta.apps.maniana.util.LanguageUtil; /** * A single page view. Contains title, date (today page only), items, and buttons. * * @author Tal Dayan */ @SuppressLint("ViewConstructor") @MainActivityScope public class PageView extends FrameLayout { /** For testing only. */ // private static final boolean FORCE_OVERFLOW_MENU_ON_ALL_DEVICES = false; /** * Candidates for day/date color. Selected by distance from background color with a slight * preference for the first one. */ private static final int[] DATE_CANDIDATE_COLORS = new int[] { 0xff222222, 0xff2222ee, 0xff22ee22, 0xff22eeee, 0xffee2222, 0xffee22ee, 0xffeeee22, 0xffeeeeee }; private static final int[] OVERFLOW_DRAWABLE_CANDIDATE_COLORS = new int[] { 0xff707070, 0xff909090 }; private static final int[] OVERFLOW_RESOURCES_CANDIDATE_IDS = new int[] { R.drawable.main_menu_button_version_1, R.drawable.main_menu_button_version_2 }; private static final int[] ITEM_HIGHLIGHT_CANDIDATE_COLORS = new int[] { 0xff0000ff, 0xffff0000, 0xff00ff00, 0xffffff00 }; private static final int[] ITEM_HIGHLIGHT_RESOURCES_CANDIDATE_IDS = new int[] { R.drawable.item_highlight_blue, R.drawable.item_highlight_red, R.drawable.item_highlight_green, R.drawable.item_highlight_yellow }; private final MainActivityState mMainActivityState; private final PageKind mPageKind; private final float mDensity; /** The sub view with the item list. */ private final ItemListView mItemListView; private final View mPageTitleSection; private final View mPageTitleDivider; private final ImageButton mMainMenuButtonView; private final ImageButton mButtonUndoView; private final ImageButton mButtonAddByTextView; private final ImageButton mButtonAddByVoiceView; private final ImageButton mButtonCleanView; private final View mDateTimeSection; private final TextView mDayTextView; private final TextView mDateTextView; private final ExtendedTextView mPageTitleTextView; private final View mPaperColorView; public PageView(MainActivityState mainActivityState, PageKind pageKind) { super(checkNotNull(mainActivityState.context())); mMainActivityState = mainActivityState; mPageKind = pageKind; mDensity = DisplayUtil.getDensity(mainActivityState.context()); mMainActivityState.services().layoutInflater().inflate(R.layout.page_layout, this); mPaperColorView = findViewById(R.id.page_paper_color); mPageTitleSection = findViewById(R.id.page_title_section); mPageTitleDivider = findViewById(R.id.page_title_divider); // mPageTitleSection.setOnClickListener(mCalendarLaunchListener); mButtonUndoView = (ImageButton) findViewById(R.id.page_undo_button); mButtonAddByTextView = (ImageButton) findViewById(R.id.page_add_by_text_button); mButtonAddByVoiceView = (ImageButton) findViewById(R.id.page_add_by_voice_button); mButtonCleanView = (ImageButton) findViewById(R.id.page_clean_button); mMainMenuButtonView = (ImageButton) findViewById(R.id.page_main_menu_button); // NOTE: could also use !ViewConfiguration.get(context).hasPermanentMenuKey(); // mUsesIcsMenuOverflowButton = FORCE_OVERFLOW_MENU_ON_ALL_DEVICES // || (android.os.Build.VERSION.SDK_INT >= 11); mDayTextView = (TextView) findViewById(R.id.page_day_text); mDateTextView = (TextView) findViewById(R.id.page_date_text); mItemListView = (ItemListView) findViewById(R.id.page_item_list); mPageTitleTextView = (ExtendedTextView) findViewById(R.id.page_title_text); mPageTitleTextView.setText(mPageKind.isToday() ? R.string.page_title_Today : R.string.page_title_Maniana); // Tomorrow page does not display date mDateTimeSection = findViewById(R.id.page_date_time_section); if (mPageKind.isToday()) { mDateTimeSection.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { onCalendarLaunchClick(); } }); } else { mDateTimeSection.setVisibility(View.GONE); } final ItemListViewAdapter adapter = new ItemListViewAdapter(mMainActivityState, mPageKind); mItemListView.setApp(mMainActivityState, adapter); mMainMenuButtonView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showMainMenu(); } }); updateUndoButton(); mButtonUndoView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mMainActivityState.controller().onUndoButton(mPageKind); } }); if (MainActivityServices.isVoiceRecognitionSupported(mMainActivityState.context())) { mButtonAddByVoiceView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mMainActivityState.controller().onAddItemByVoiceButton(mPageKind); } }); } else { mButtonAddByVoiceView.setVisibility(View.GONE); } mButtonAddByTextView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mMainActivityState.controller().onAddItemByTextButton(mPageKind); } }); mButtonCleanView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mMainActivityState.controller().onCleanPageButton(mPageKind, false); } }); mButtonCleanView.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { mMainActivityState.controller().onCleanPageButton(mPageKind, true); return true; } }); // Set initial preferences onPageIconSetPreferenceChange(); onPageBackgroundPreferenceChange(); onPageTitlePreferenceChange(); onItemDividerColorPreferenceChange(); } void showMainMenu() { final MainMenu mainMenu = new MainMenu(mMainActivityState, new MainMenu.OnActionItemOutcomeListener() { @Override public void onOutcome(MainMenu source, MainMenuEntry selectedEntry) { mMainActivityState.controller().onMainMenuSelection(selectedEntry); } }); mainMenu.show(mMainMenuButtonView); } // TODO: currently the controller calls this even when there is not date change. Filter // it somewhere? public final void onDateChange() { // Only Today page has date. check(mPageKind.isToday()); mDayTextView.setText(mMainActivityState.dateTracker().getUserDayOfWeekString()); mDateTextView.setText(mMainActivityState.dateTracker().getUserMonthDayString()); } /** Called when the user clicked on a calendar launch area. */ private final void onCalendarLaunchClick() { mMainActivityState.controller().onCalendarLaunchClick(); } public final void onPageItemFontVariationPreferenceChange() { mItemListView.onPageItemFontVariationPreferenceChange(); } public final void onPageIconSetPreferenceChange() { final PageIconSet iconSet = mMainActivityState.prefTracker().getPageIconSetPreference(); mButtonUndoView.setImageResource(iconSet.buttonUndoResourceId); mButtonAddByTextView.setImageResource(iconSet.buttonAddByTextResourceId); mButtonAddByVoiceView.setImageResource(iconSet.buttonAddByVoiceResourceId); mButtonCleanView.setImageResource(iconSet.buttonCleanResourceId); // Since the icon set affects also the item arrow icons, we need to update // the items as well. updateAllItemViews(); } /** * Update page background to current preferences. * * This method is called only when preferences change so we don't bother comparing to current * state to avoid unnecessary changes. */ public final void onPageBackgroundPreferenceChange() { // Set the background color or image final int baseBackgroundColor; if (mMainActivityState.prefTracker().getBackgroundPaperPreference()) { final int backgroundImageId = (mPageKind.isToday()) ? R.drawable.page_bg_left : R.drawable.page_bg_right; setBackgroundResource(backgroundImageId); final int paperColor = mMainActivityState.prefTracker().getPagePaperColorPreference(); mPaperColorView.setBackgroundColor(ColorUtil.mapPaperColorPrefernce(paperColor)); baseBackgroundColor = paperColor; } else { final int backgroundColor = mMainActivityState.prefTracker() .getPageBackgroundSolidColorPreference(); setBackgroundColor(backgroundColor); mPaperColorView.setBackgroundColor(0x00000000); baseBackgroundColor = backgroundColor; } // Update date text to have max contrast from the background if (mPageKind.isToday()) { final int dayAndDateColor = ColorUtil.selectFurthestColor(baseBackgroundColor, DATE_CANDIDATE_COLORS, 0.05f); mDayTextView.setTextColor(dayAndDateColor); mDateTextView.setTextColor(dayAndDateColor); } // Update ICS menu overflow icon to have max contrast from the background // if (mUsesIcsMenuOverflowButton) { final int colorIndex = ColorUtil.selectFurthestColorIndex(baseBackgroundColor, OVERFLOW_DRAWABLE_CANDIDATE_COLORS, 0.05f); final int resourceId = OVERFLOW_RESOURCES_CANDIDATE_IDS[colorIndex]; mMainMenuButtonView.setImageResource(resourceId); } // Update item list view highlight drawable to max contrast from page background { final int colorIndex = ColorUtil.selectFurthestColorIndex(baseBackgroundColor, ITEM_HIGHLIGHT_CANDIDATE_COLORS, 0.05f); final int resourceId = ITEM_HIGHLIGHT_RESOURCES_CANDIDATE_IDS[colorIndex]; mItemListView.setItemHighlightDrawableResourceId(resourceId); } } public final void onPageTitlePreferenceChange() { mPageTitleTextView.setTextColor(mPageKind.isToday() ? mMainActivityState.prefTracker() .getPageTitleTodayColor() : mMainActivityState.prefTracker() .getPageTitleTomorowColor()); final Font titleFont = mMainActivityState.prefTracker().getPageTitleFontPreference(); final TypefaceSpec titleFontSpec = titleFont.getTypefaceSpec(mMainActivityState.context()); // French 'Today' title does not fit on Galaxy Nexus with default title font and size (it's // a long word) so we scale it down in both pages. final float frenchFactor = LanguageUtil.currentLanguageIsFrench(mMainActivityState .context()) ? 0.85f : 1.0f; final float titleFontSizeSP = mMainActivityState.prefTracker() .getPageTitleFontSizePreference() * titleFontSpec.scale * frenchFactor; mPageTitleTextView.setTypeface(titleFontSpec.typeface); mPageTitleTextView.setExtraSpacingFractions(titleFontSpec.topExtraSpacingFraction, titleFontSpec.bottomExtraSpacingFraction); mPageTitleTextView.setTextSize(titleFontSizeSP); // Match vertical padding to title text size final int bottomPaddingPixels = (int) (Math.max(8, titleFontSizeSP * 0.12f) * mDensity); mPageTitleSection.setPadding(0, 0, 0, bottomPaddingPixels); // Update date size to match title size if (mPageKind.isToday()) { final int dayTopPaddingPixels = (int) (titleFontSizeSP * mDensity * 0.16f + 0.5f); final float dayTextSize = Math.min(20, Math.max(14, titleFontSizeSP * 0.35f)); final float dateTextSize = Math.min(16, Math.max(14, titleFontSizeSP * 0.30f)); mDayTextView.setPadding(0, dayTopPaddingPixels, 0, 0); mDayTextView.setTextSize(dayTextSize); mDateTextView.setTextSize(dateTextSize); } } /** Make the given item visible. */ public void scrollToItem(int itemIndex) { mItemListView.scrollToItem(itemIndex); } /** Turn item view highlight on/off. */ public void setItemViewHighlight(int itemIndex, boolean isHighlight) { mItemListView.setItemViewHighlight(itemIndex, isHighlight); } /** * Start animating given item. * * @param initialDelayMillis - wait this time in milliseconds before starting the animation. * Note that the method returns immediately even if a non zero delay is specified. */ public final void startItemAnimation(int itemIndex, AppView.ItemAnimationType animationType, int initialDelayMillis, @Nullable Runnable callback) { mItemListView.startItemAnimation(itemIndex, animationType, initialDelayMillis, callback); } /** Popup an item menu for the given item. */ public void showItemMenu(final int itemIndex, ItemMenuEntry actions[], final int dismissActionId) { mItemListView.showItemMenu(itemIndex, actions, dismissActionId); } /** * Update the item list to reflect the current model state. Does not affect other parts of the * page. */ public final void updateAllItemViews() { // We simply force the model to send an updated notifcation. mItemListView.getAdapter().notifyDataSetChanged(); } /** Update undo button bases on the current model state. */ public final void updateUndoButton() { final boolean hasUndo = mMainActivityState.model().pageHasUndo(mPageKind); mButtonUndoView.setVisibility(hasUndo ? VISIBLE : INVISIBLE); } /** Update item divider color on preference change */ public final void onItemDividerColorPreferenceChange() { final int dividerColor = mMainActivityState.prefTracker() .getPageItemDividerColorPreference(); // The alpha at the edge is 25% of the center alpha final int endGradiantColor = (dividerColor & 0x00ffffff) | ((dividerColor >> 3) & 0x1f000000); final int colors[] = new int[] { endGradiantColor, dividerColor, endGradiantColor }; final GradientDrawable drawable = new GradientDrawable( GradientDrawable.Orientation.LEFT_RIGHT, colors); drawable.setShape(GradientDrawable.RECTANGLE); drawable.setGradientType(GradientDrawable.LINEAR_GRADIENT); final Drawable cloneDrawable = drawable.getConstantState().newDrawable(); mPageTitleDivider.setBackgroundDrawable(cloneDrawable); mItemListView.setDivider(drawable); mItemListView.setDividerHeight(2); } public final int getItemCount() { return mItemListView.getChildCount(); } }