Android Open Source - Roundr Window






From Project

Back to project page Roundr.

License

The source code is released under:

Copyright (C) 2013 Mohammad Adib Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the te...

If you think the Android project Roundr listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package wei.mark.standout.ui;
/*from   w w  w  . j  a  v  a 2 s  .c o  m*/
import java.util.LinkedList;
import java.util.Queue;

import wei.mark.standout.R;
import wei.mark.standout.StandOutWindow;
import wei.mark.standout.StandOutWindow.StandOutLayoutParams;
import wei.mark.standout.Utils;
import wei.mark.standout.constants.StandOutFlags;
import android.content.Context;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.TextView;

/**
 * Special view that represents a floating window.
 * 
 * @author Mark Wei <markwei@gmail.com> Edited by: Mohammad Adib
 * 
 */
public class Window extends FrameLayout {
  public static final int VISIBILITY_GONE = 0;
  public static final int VISIBILITY_VISIBLE = 1;
  public static final int VISIBILITY_TRANSITION = 2;

  static final String TAG = "Window";

  /**
   * Class of the window, indicating which application the window belongs to.
   */
  public Class<? extends StandOutWindow> cls;
  /**
   * Id of the window.
   */
  public int id;

  /**
   * Whether the window is shown, hidden/closed, or in transition.
   */
  public int visibility;

  /**
   * Whether the window is focused.
   */
  public boolean focused;

  /**
   * Original params from {@link StandOutWindow#getParams(int, Window)}.
   */
  public StandOutLayoutParams originalParams;
  /**
   * Original flags from {@link StandOutWindow#getFlags(int)}.
   */
  public int flags;

  /**
   * Touch information of the window.
   */
  public TouchInfo touchInfo;

  /**
   * Data attached to the window.
   */
  public Bundle data;

  /**
   * Context of the window.
   */
  private final StandOutWindow mContext;
  private LayoutInflater mLayoutInflater;

  public Window(Context context) {
    super(context);
    mContext = null;
  }

  public Window(final StandOutWindow context, final int id) {
    super(context);
    context.setTheme(context.getThemeStyle());

    mContext = context;
    mLayoutInflater = LayoutInflater.from(context);

    this.cls = context.getClass();
    this.id = id;
    this.originalParams = context.getParams(id, this);
    this.flags = context.getFlags(id);
    this.touchInfo = new TouchInfo();
    touchInfo.ratio = (float) originalParams.width / originalParams.height;
    this.data = new Bundle();

    // create the window contents
    View content;
    FrameLayout body;

    if (Utils.isSet(flags, StandOutFlags.FLAG_DECORATION_SYSTEM)) {
      // requested system window decorations
      content = getSystemDecorations();
      body = (FrameLayout) content.findViewById(R.id.body);
    } else {
      // did not request decorations. will provide own implementation
      content = new FrameLayout(context);
      content.setId(R.id.content);
      body = (FrameLayout) content;
    }

    addView(content);

    body.setOnTouchListener(new OnTouchListener() {

      @Override
      public boolean onTouch(View v, MotionEvent event) {
        // pass all touch events to the implementation
        boolean consumed = false;

        // handle move and bring to front
        consumed = context.onTouchHandleMove(id, Window.this, v, event) || consumed;

        // alert implementation
        consumed = context.onTouchBody(id, Window.this, v, event) || consumed;

        return consumed;
      }
    });

    // attach the view corresponding to the id from the
    // implementation
    context.createAndAttachView(id, body);

    // make sure the implementation attached the view
    if (body.getChildCount() == 0) {
      throw new RuntimeException("You must attach your view to the given frame in createAndAttachView()");
    }

    // implement StandOut specific workarounds
    if (!Utils.isSet(flags, StandOutFlags.FLAG_FIX_COMPATIBILITY_ALL_DISABLE)) {
      fixCompatibility(body);
    }
    // implement StandOut specific additional functionality
    if (!Utils.isSet(flags, StandOutFlags.FLAG_ADD_FUNCTIONALITY_ALL_DISABLE)) {
      addFunctionality(body);
    }

    // attach the existing tag from the frame to the window
    setTag(body.getTag());
  }

  @Override
  public boolean onInterceptTouchEvent(MotionEvent event) {
    StandOutLayoutParams params = getLayoutParams();

    // focus window
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
      if (mContext.getFocusedWindow() != this) {
        mContext.focus(id);
      }
    }

    // multitouch
    if (event.getPointerCount() >= 2 && Utils.isSet(flags, StandOutFlags.FLAG_WINDOW_PINCH_RESIZE_ENABLE) && (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
      touchInfo.scale = 1;
      touchInfo.dist = -1;
      touchInfo.firstWidth = params.width;
      touchInfo.firstHeight = params.height;
      return true;
    }

    return false;
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    // handle touching outside
    switch (event.getAction()) {
    case MotionEvent.ACTION_OUTSIDE:
      // unfocus window
      if (mContext.getFocusedWindow() == this) {
        mContext.unfocus(this);
      }

      // notify implementation that ACTION_OUTSIDE occurred
      mContext.onTouchBody(id, this, this, event);
      break;
    }

    // handle multitouch
    if (event.getPointerCount() >= 2 && Utils.isSet(flags, StandOutFlags.FLAG_WINDOW_PINCH_RESIZE_ENABLE)) {
      // 2 fingers or more

      float x0 = event.getX(0);
      float y0 = event.getY(0);
      float x1 = event.getX(1);
      float y1 = event.getY(1);

      double dist = Math.sqrt(Math.pow(x0 - x1, 2) + Math.pow(y0 - y1, 2));

      switch (event.getAction() & MotionEvent.ACTION_MASK) {
      case MotionEvent.ACTION_MOVE:
        if (touchInfo.dist == -1) {
          touchInfo.dist = dist;
        }
        touchInfo.scale *= dist / touchInfo.dist;
        touchInfo.dist = dist;

        // scale the window with anchor point set to middle
        edit().setAnchorPoint(.5f, .5f).setSize((int) (touchInfo.firstWidth * touchInfo.scale), (int) (touchInfo.firstHeight * touchInfo.scale)).commit();
        break;
      }
    }

    return true;
  }

  @Override
  public boolean dispatchKeyEvent(KeyEvent event) {
    if (mContext.onKeyEvent(id, this, event)) {
      Log.d(TAG, "Window " + id + " key event " + event + " cancelled by implementation.");
      return false;
    }

    if (event.getAction() == KeyEvent.ACTION_UP) {
      switch (event.getKeyCode()) {
      case KeyEvent.KEYCODE_BACK:
        mContext.unfocus(this);
        return true;
      }
    }

    return super.dispatchKeyEvent(event);
  }

  /**
   * Request or remove the focus from this window.
   * 
   * @param focus
   *            Whether we want to gain or lose focus.
   * @return True if focus changed successfully, false if it failed.
   */
  public boolean onFocus(boolean focus) {
    if (!Utils.isSet(flags, StandOutFlags.FLAG_WINDOW_FOCUSABLE_DISABLE)) {
      // window is focusable

      if (focus == focused) {
        // window already focused/unfocused
        return false;
      }

      focused = focus;

      // alert callbacks and cancel if instructed
      if (mContext.onFocusChange(id, this, focus)) {
        Log.d(TAG, "Window " + id + " focus change " + (focus ? "(true)" : "(false)") + " cancelled by implementation.");
        focused = !focus;
        return false;
      }

      if (!Utils.isSet(flags, StandOutFlags.FLAG_WINDOW_FOCUS_INDICATOR_DISABLE)) {
        // change visual state
        View content = findViewById(R.id.content);
        if (focus) {
          // gaining focus
          content.setBackgroundResource(R.drawable.border_focused);
        } else {
          // losing focus
          if (Utils.isSet(flags, StandOutFlags.FLAG_DECORATION_SYSTEM)) {
            // system decorations
            content.setBackgroundResource(R.drawable.border);
          } else {
            // no decorations
            content.setBackgroundResource(0);
          }
        }
      }

      // set window manager params
      StandOutLayoutParams params = getLayoutParams();
      params.setFocusFlag(focus);
      mContext.updateViewLayout(id, params);

      if (focus) {
        mContext.setFocusedWindow(this);
      } else {
        if (mContext.getFocusedWindow() == this) {
          mContext.setFocusedWindow(null);
        }
      }

      return true;
    }
    return false;
  }

  @Override
  public void setLayoutParams(ViewGroup.LayoutParams params) {
    if (params instanceof StandOutLayoutParams) {
      super.setLayoutParams(params);
    } else {
      throw new IllegalArgumentException("Window" + id + ": LayoutParams must be an instance of StandOutLayoutParams.");
    }
  }

  /**
   * Convenience method to start editting the size and position of this
   * window. Make sure you call {@link Editor#commit()} when you are done to
   * update the window.
   * 
   * @return The Editor associated with this window.
   */
  public Editor edit() {
    return new Editor();
  }

  @Override
  public StandOutLayoutParams getLayoutParams() {
    StandOutLayoutParams params = (StandOutLayoutParams) super.getLayoutParams();
    if (params == null) {
      params = originalParams;
    }
    return params;
  }

  /**
   * Returns the system window decorations if the implementation sets
   * {@link #FLAG_DECORATION_SYSTEM}.
   * 
   * <p>
   * The system window decorations support hiding, closing, moving, and
   * resizing.
   * 
   * @return The frame view containing the system window decorations.
   */
  private View getSystemDecorations() {
    final View decorations = mLayoutInflater.inflate(R.layout.system_window_decorators, null);

    // icon
    final ImageView icon = (ImageView) decorations.findViewById(R.id.window_icon);
    icon.setImageResource(mContext.getAppIcon());
    icon.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        PopupWindow dropDown = mContext.getDropDown(id);
        if (dropDown != null) {
          dropDown.showAsDropDown(icon);
        }
      }
    });

    // title
    TextView title = (TextView) decorations.findViewById(R.id.title);
    title.setText(mContext.getTitle(id));

    // hide
    View hide = decorations.findViewById(R.id.hide);
    hide.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        mContext.hide(id);
      }
    });
    hide.setVisibility(View.GONE);

    // close
    View close = decorations.findViewById(R.id.close);
    close.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {
        mContext.close(id);
      }
    });

    // move
    View titlebar = decorations.findViewById(R.id.titlebar);
    titlebar.setOnTouchListener(new OnTouchListener() {

      @Override
      public boolean onTouch(View v, MotionEvent event) {
        // handle dragging to move
        boolean consumed = mContext.onTouchHandleMove(id, Window.this, v, event);
        return consumed;
      }
    });

    // resize
    View corner = decorations.findViewById(R.id.corner);
    corner.setOnTouchListener(new OnTouchListener() {

      @Override
      public boolean onTouch(View v, MotionEvent event) {
        // handle dragging to move
        boolean consumed = mContext.onTouchHandleResize(id, Window.this, v, event);

        return consumed;
      }
    });

    // set window appearance and behavior based on flags
    if (Utils.isSet(flags, StandOutFlags.FLAG_WINDOW_HIDE_ENABLE)) {
      hide.setVisibility(View.VISIBLE);
    }
    if (Utils.isSet(flags, StandOutFlags.FLAG_DECORATION_CLOSE_DISABLE)) {
      close.setVisibility(View.GONE);
    }
    if (Utils.isSet(flags, StandOutFlags.FLAG_DECORATION_MOVE_DISABLE)) {
      titlebar.setOnTouchListener(null);
    }
    if (Utils.isSet(flags, StandOutFlags.FLAG_DECORATION_RESIZE_DISABLE)) {
      corner.setVisibility(View.GONE);
    }

    return decorations;
  }

  /**
   * Implement StandOut specific additional functionalities.
   * 
   * <p>
   * Currently, this method does the following:
   * 
   * <p>
   * Attach resize handles: For every View found to have id R.id.corner,
   * attach an OnTouchListener that implements resizing the window.
   * 
   * @param root
   *            The view hierarchy that is part of the window.
   */
  void addFunctionality(View root) {
    // corner for resize
    if (!Utils.isSet(flags, StandOutFlags.FLAG_ADD_FUNCTIONALITY_RESIZE_DISABLE)) {
      View corner = root.findViewById(R.id.corner);
      if (corner != null) {
        corner.setOnTouchListener(new OnTouchListener() {

          @Override
          public boolean onTouch(View v, MotionEvent event) {
            // handle dragging to move
            boolean consumed = mContext.onTouchHandleResize(id, Window.this, v, event);

            return consumed;
          }
        });
      }
    }

    // window_icon for drop down
    if (!Utils.isSet(flags, StandOutFlags.FLAG_ADD_FUNCTIONALITY_DROP_DOWN_DISABLE)) {
      final View icon = root.findViewById(R.id.window_icon);
      if (icon != null) {
        icon.setOnClickListener(new OnClickListener() {

          @Override
          public void onClick(View v) {
            PopupWindow dropDown = mContext.getDropDown(id);
            if (dropDown != null) {
              dropDown.showAsDropDown(icon);
            }
          }
        });
      }
    }
  }

  /**
   * Iterate through each View in the view hiearchy and implement StandOut
   * specific compatibility workarounds.
   * 
   * <p>
   * Currently, this method does the following:
   * 
   * <p>
   * Nothing yet.
   * 
   * @param root
   *            The root view hierarchy to iterate through and check.
   */
  void fixCompatibility(View root) {
    Queue<View> queue = new LinkedList<View>();
    queue.add(root);

    View view = null;
    while ((view = queue.poll()) != null) {
      // do nothing yet

      // iterate through children
      if (view instanceof ViewGroup) {
        ViewGroup group = (ViewGroup) view;
        for (int i = 0; i < group.getChildCount(); i++) {
          queue.add(group.getChildAt(i));
        }
      }
    }
  }

  /**
   * Convenient way to resize or reposition a Window. The Editor allows you to
   * easily resize and reposition the window around anchor points.
   * 
   * @author Mark Wei <markwei@gmail.com>
   * 
   */
  public class Editor {
    /**
     * Special value for width, height, x, or y positions that represents
     * that the value should not be changed.
     */
    public static final int UNCHANGED = Integer.MIN_VALUE;

    /**
     * Layout params of the window associated with this Editor.
     */
    StandOutLayoutParams mParams;

    /**
     * The relative position of the anchor point. The anchor point is only
     * used by the {@link Editor}.
     * 
     * <p>
     * The anchor point effects the following methods:
     * 
     * <p>
     * {@link #setSize(int, int)} and {@link #setSize(float, float)}. The
     * window will expand or shrink around the anchor point.
     * 
     * <p>
     * Values must be between 0 and 1, inclusive. 0 means the left/top, 0.5
     * is the center, 1 is the right/bottom.
     */
    float anchorX, anchorY;

    /**
     * Width and height of the screen.
     */
    int displayWidth, displayHeight;

    public Editor() {
      mParams = getLayoutParams();
      anchorX = anchorY = 0;

      DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
      displayWidth = metrics.widthPixels;
      displayHeight = (int) (metrics.heightPixels - 25 * metrics.density);
    }

    public Editor setAnchorPoint(float x, float y) {
      if (x < 0 || x > 1 || y < 0 || y > 1) {
        throw new IllegalArgumentException("Anchor point must be between 0 and 1, inclusive.");
      }

      anchorX = x;
      anchorY = y;

      return this;
    }

    /**
     * Set the size of this window in absolute pixels.
     * 
     * @param width
     * @param height
     * @return The same Editor, useful for method chaining.
     */
    public Editor setSize(int width, int height) {
      return setSize(width, height, false);
    }

    /**
     * Set the size of this window in absolute pixels.
     * 
     * @param width
     * @param height
     * @param skip
     *            Don't call {@link #setPosition(int, int)} to avoid stack
     *            overflow.
     * @return The same Editor, useful for method chaining.
     */
    private Editor setSize(int width, int height, boolean skip) {
      if (mParams != null) {
        if (anchorX < 0 || anchorX > 1 || anchorY < 0 || anchorY > 1) {
          throw new IllegalStateException("Anchor point must be between 0 and 1, inclusive.");
        }

        int lastWidth = mParams.width;
        int lastHeight = mParams.height;

        if (width != UNCHANGED) {
          mParams.width = width;
        }
        if (height != UNCHANGED) {
          mParams.height = height;
        }

        // set max width/height
        int maxWidth = mParams.maxWidth;
        int maxHeight = mParams.maxHeight;

        if (Utils.isSet(flags, StandOutFlags.FLAG_WINDOW_EDGE_LIMITS_ENABLE)) {
          maxWidth = (int) Math.min(maxWidth, displayWidth);
          maxHeight = (int) Math.min(maxHeight, displayHeight);
        }

        // keep window between min and max
        mParams.width = Math.min(Math.max(mParams.width, mParams.minWidth), maxWidth);
        mParams.height = Math.min(Math.max(mParams.height, mParams.minHeight), maxHeight);

        // keep window in aspect ratio
        if (Utils.isSet(flags, StandOutFlags.FLAG_WINDOW_ASPECT_RATIO_ENABLE)) {
          int ratioWidth = (int) (mParams.height * touchInfo.ratio);
          int ratioHeight = (int) (mParams.width / touchInfo.ratio);
          if (ratioHeight >= mParams.minHeight && ratioHeight <= mParams.maxHeight) {
            // width good adjust height
            mParams.height = ratioHeight;
          } else {
            // height good adjust width
            mParams.width = ratioWidth;
          }
        }

        if (!skip) {
          // set position based on anchor point
          setPosition((int) (mParams.x + lastWidth * anchorX), (int) (mParams.y + lastHeight * anchorY));
        }
      }

      return this;
    }

    /**
     * Set the position of this window in absolute pixels.
     * 
     * @param x
     * @param y
     * @return The same Editor, useful for method chaining.
     */
    public Editor setPosition(int x, int y) {
      return setPosition(x, y, false);
    }

    /**
     * Set the position of this window in absolute pixels.
     * 
     * @param x
     * @param y
     * @param skip
     *            Don't call {@link #setPosition(int, int)} and
     *            {@link #setSize(int, int)} to avoid stack overflow.
     * @return The same Editor, useful for method chaining.
     */
    private Editor setPosition(int x, int y, boolean skip) {
      if (mParams != null) {
        if (anchorX < 0 || anchorX > 1 || anchorY < 0 || anchorY > 1) {
          throw new IllegalStateException("Anchor point must be between 0 and 1, inclusive.");
        }

        // sets the x and y correctly according to anchorX and
        // anchorY
        if (x != UNCHANGED) {
          mParams.x = (int) (x - mParams.width * anchorX);
        }
        if (y != UNCHANGED) {
          mParams.y = (int) (y - mParams.height * anchorY);
        }

        if (Utils.isSet(flags, StandOutFlags.FLAG_WINDOW_EDGE_LIMITS_ENABLE)) {
          // if gravity is not TOP|LEFT throw exception
          if (mParams.gravity != (Gravity.TOP | Gravity.LEFT)) {
            throw new IllegalStateException("The window " + id + " gravity must be TOP|LEFT if FLAG_WINDOW_EDGE_LIMITS_ENABLE or FLAG_WINDOW_EDGE_TILE_ENABLE is set.");
          }

          // keep window inside edges
          mParams.x = Math.min(Math.max(mParams.x, 0), displayWidth - mParams.width);
          mParams.y = Math.min(Math.max(mParams.y, 0), displayHeight - mParams.height);
        }
      }

      return this;
    }

    /**
     * Commit the changes to this window. Updates the layout. This Editor
     * cannot be used after you commit.
     */
    public void commit() {
      if (mParams != null) {
        mContext.updateViewLayout(id, mParams);
        mParams = null;
      }
    }
  }
}




Java Source Code List

mohammad.adib.roundr.BootReceiver.java
mohammad.adib.roundr.Corner.java
mohammad.adib.roundr.MainActivity.java
mohammad.adib.roundr.SeekBarPreference.java
mohammad.adib.roundr.SettingsActivity.java
wei.mark.standout.StandOutWindow.java
wei.mark.standout.Utils.java
wei.mark.standout.WindowCache.java
wei.mark.standout.constants.StandOutFlags.java
wei.mark.standout.ui.TouchInfo.java
wei.mark.standout.ui.Window.java