@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); // Target placement width/height. This puts the targets on the greater of the ring // width or the specified outer radius. final float placementWidth = getRingWidth(); final float placementHeight = getRingHeight(); float newWaveCenterX = mHorizontalInset + (mMaxTargetWidth + placementWidth) / 2; float newWaveCenterY = mVerticalInset + (mMaxTargetHeight + placementHeight) / 2; if (mInitialLayout) { stopAndHideWaveAnimation(); hideTargets(false, false); mInitialLayout = false; } mOuterRing.setPositionX(newWaveCenterX); mOuterRing.setPositionY(newWaveCenterY); mPointCloud.setScale(mRingScaleFactor); mHandleDrawable.setPositionX(newWaveCenterX); mHandleDrawable.setPositionY(newWaveCenterY); updateTargetPositions(newWaveCenterX, newWaveCenterY); updatePointCloudPosition(newWaveCenterX, newWaveCenterY); updateGlowPosition(newWaveCenterX, newWaveCenterY); mWaveCenterX = newWaveCenterX; mWaveCenterY = newWaveCenterY; if (DEBUG) dump(); }
private void handleDown(MotionEvent event) { int actionIndex = event.getActionIndex(); float eventX = event.getX(actionIndex); float eventY = event.getY(actionIndex); switchToState(STATE_START, eventX, eventY); if (!trySwitchToFirstTouchState(eventX, eventY)) { mDragging = false; } else { mPointerId = event.getPointerId(actionIndex); updateGlowPosition(eventX, eventY); } }
private boolean trySwitchToFirstTouchState(float x, float y) { final float tx = x - mWaveCenterX; final float ty = y - mWaveCenterY; if (mAlwaysTrackFinger || dist2(tx, ty) <= getScaledGlowRadiusSquared()) { if (DEBUG) Log.v(TAG, "** Handle HIT"); switchToState(STATE_FIRST_TOUCH, x, y); updateGlowPosition(tx, ty); mDragging = true; return true; } return false; }
private void handleMove(MotionEvent event) { int activeTarget = -1; final int historySize = event.getHistorySize(); ArrayList<TargetDrawable> targets = mTargetDrawables; int ntargets = targets.size(); float x = 0.0f; float y = 0.0f; float activeAngle = 0.0f; int actionIndex = event.findPointerIndex(mPointerId); if (actionIndex == -1) { return; // no data for this pointer } for (int k = 0; k < historySize + 1; k++) { float eventX = k < historySize ? event.getHistoricalX(actionIndex, k) : event.getX(actionIndex); float eventY = k < historySize ? event.getHistoricalY(actionIndex, k) : event.getY(actionIndex); // tx and ty are relative to wave center float tx = eventX - mWaveCenterX; float ty = eventY - mWaveCenterY; float touchRadius = (float) Math.sqrt(dist2(tx, ty)); final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f; float limitX = tx * scale; float limitY = ty * scale; double angleRad = Math.atan2(-ty, tx); if (!mDragging) { trySwitchToFirstTouchState(eventX, eventY); } if (mDragging) { // For multiple targets, snap to the one that matches final float snapRadius = mRingScaleFactor * mOuterRadius - mSnapMargin; final float snapDistance2 = snapRadius * snapRadius; // Find first target in range for (int i = 0; i < ntargets; i++) { TargetDrawable target = targets.get(i); double targetMinRad = mFirstItemOffset + (i - 0.5) * 2 * Math.PI / ntargets; double targetMaxRad = mFirstItemOffset + (i + 0.5) * 2 * Math.PI / ntargets; if (target.isEnabled()) { boolean angleMatches = (angleRad > targetMinRad && angleRad <= targetMaxRad) || (angleRad + 2 * Math.PI > targetMinRad && angleRad + 2 * Math.PI <= targetMaxRad) || (angleRad - 2 * Math.PI > targetMinRad && angleRad - 2 * Math.PI <= targetMaxRad); if (angleMatches && (dist2(tx, ty) > snapDistance2)) { activeTarget = i; activeAngle = (float) -angleRad; } } } } x = limitX; y = limitY; } if (!mDragging) { return; } if (activeTarget != -1) { switchToState(STATE_SNAP, x, y); updateGlowPosition(x, y); } else { switchToState(STATE_TRACKING, x, y); updateGlowPosition(x, y); } if (mActiveTarget != activeTarget) { // Defocus the old target if (mActiveTarget != -1) { TargetDrawable target = targets.get(mActiveTarget); if (target.hasState(TargetDrawable.STATE_FOCUSED)) { target.setState(TargetDrawable.STATE_INACTIVE); } if (mMagneticTargets) { updateTargetPosition(mActiveTarget, mWaveCenterX, mWaveCenterY); } } // Focus the new target if (activeTarget != -1) { TargetDrawable target = targets.get(activeTarget); if (target.hasState(TargetDrawable.STATE_FOCUSED)) { target.setState(TargetDrawable.STATE_FOCUSED); } if (mMagneticTargets) { updateTargetPosition(activeTarget, mWaveCenterX, mWaveCenterY, activeAngle); } if (AccessibilityManager.getInstance(mContext).isEnabled()) { String targetContentDescription = getTargetDescription(activeTarget); announceForAccessibility(targetContentDescription); } } } mActiveTarget = activeTarget; }