Hi i'm a beginner in android app development.
I need help making my app. so my app is a simple Timer with progress bar which works fine. Now i need to make that "progress bar's" to draw over other apps even if the app is minimized or closed until the timer expires.
So for this I tried many things in many ways like i used Window Manager and other things in a service class, i even separated progress bar into different layout.
help me with code. the following contains code which works fine within app.
1)Main Activity.java
import android.animation.ObjectAnimator;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private long timeCountInMilliSeconds = 1 * 60000;
private enum TimerStatus {
STARTED,
STOPPED
}
private TimerStatus timerStatus = TimerStatus.STOPPED;
private ProgressBar progressBar;
private ProgressBar progressBar1;
private EditText editTextMinute;
private TextView textViewTime;
private ImageView imageViewReset;
private ImageView imageViewStartStop;
private CountDownTimer countDownTimer;
private ObjectAnimator smoothAnimation;
private ObjectAnimator smoothAnimation1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// method call to initialize the views
initViews();
// method call to initialize the listeners
initListeners();
}
/**
* method to initialize the views
*/
private void initViews() {
progressBar = findViewById(R.id.progress_bar);
progressBar1 = findViewById(R.id.progress_bar1);
editTextMinute = findViewById(R.id.editTextMinute);
textViewTime = findViewById(R.id.textViewTime);
imageViewReset = findViewById(R.id.imageViewReset);
imageViewStartStop = findViewById(R.id.imageViewStartStop);
}
/**
* method to initialize the click listeners
*/
private void initListeners() {
imageViewReset.setOnClickListener(this);
imageViewStartStop.setOnClickListener(this);
}
/**
* implemented method to listen clicks
*
* @param view
*/
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.imageViewReset:
reset();
break;
case R.id.imageViewStartStop:
startStop();
break;
}
}
/**
* method to reset count down timer
*/
private void reset() {
stopCountDownTimer();
startCountDownTimer();
}
/**
* method to start and stop count down timer
*/
private void startStop() {
if (timerStatus == TimerStatus.STOPPED) {
// call to initialize the timer values
setTimerValues();
// call to initialize the progress bar values
setProgressBarValues();
// showing the reset icon
imageViewReset.setVisibility(View.VISIBLE);
// changing play icon to stop icon
imageViewStartStop.setImageResource(R.drawable.icon_stop);
// making edit text not editable
editTextMinute.setEnabled(false);
// changing the timer status to started
timerStatus = TimerStatus.STARTED;
// call to start the count down timer
startCountDownTimer();
} else {
// hiding the reset icon
imageViewReset.setVisibility(View.GONE);
// changing stop icon to start icon
imageViewStartStop.setImageResource(R.drawable.icon_start);
// making edit text editable
editTextMinute.setEnabled(true);
// changing the timer status to stopped
timerStatus = TimerStatus.STOPPED;
stopCountDownTimer();
}
}
/**
* method to initialize the values for count down timer
*/
private void setTimerValues() {
int time = 0;
if (!editTextMinute.getText().toString().isEmpty()) {
// fetching value from edit text and type cast to integer
time = Integer.parseInt(editTextMinute.getText().toString().trim());
} else {
// toast message to fill edit text
Toast.makeText(getApplicationContext(), getString(R.string.message_minutes), Toast.LENGTH_LONG).show();
}
// assigning values after converting to milliseconds
timeCountInMilliSeconds = time * 1000;
}
/**
* method to start count down timer
*/
private void startCountDownTimer() {
//left side Progress Bar
smoothAnimation = ObjectAnimator.ofInt(progressBar, "progress", progressBar.getProgress(), progressBar.getMax());
smoothAnimation.setDuration(5);
smoothAnimation.setInterpolator(new AccelerateInterpolator());
//right side Progress Bar
smoothAnimation1 = ObjectAnimator.ofInt(progressBar, "progress", progressBar1.getProgress(), progressBar1.getMax());
smoothAnimation1.setDuration(5);
smoothAnimation1.setInterpolator(new AccelerateInterpolator());
countDownTimer = new CountDownTimer(timeCountInMilliSeconds, 10) {
@Override
public void onTick(long millisUntilFinished) {
Log.d("TAG", "ON Tick Called "+millisUntilFinished);
textViewTime.setText(hmsTimeFormatter(millisUntilFinished+1000));
progressBar.setProgress((int) (timeCountInMilliSeconds / 10 - millisUntilFinished / 10));
progressBar1.setProgress((int) (timeCountInMilliSeconds / 10 - millisUntilFinished / 10));
}
@Override
public void onFinish() {
textViewTime.setText(hmsTimeFormatter(timeCountInMilliSeconds));
// call to initialize the progress bar values
setProgressBarValues();
// hiding the reset icon
imageViewReset.setVisibility(View.GONE);
// changing stop icon to start icon
imageViewStartStop.setImageResource(R.drawable.icon_start);
// making edit text editable
editTextMinute.setEnabled(true);
// changing the timer status to stopped
timerStatus = TimerStatus.STOPPED;
smoothAnimation.end();
}
}.start();
smoothAnimation.start();
countDownTimer.start();
}
/**
* method to stop count down timer
*/
private void stopCountDownTimer() {
countDownTimer.cancel();
}
/**
* method to set circular progress bar values
*/
private void setProgressBarValues() {
progressBar.setMax((int) (timeCountInMilliSeconds / 10));
progressBar.setProgress((int) (timeCountInMilliSeconds / 10));
progressBar1.setMax((int) (timeCountInMilliSeconds / 10));
progressBar1.setProgress((int) (timeCountInMilliSeconds / 10));
}
/**
* method to convert millisecond to time format
*
* @param milliSeconds
* @return HH:mm:ss time formatted string
*/
private String hmsTimeFormatter(long milliSeconds) {
String hms = String.format("%02d:%02d:%02d",
TimeUnit.MILLISECONDS.toHours(milliSeconds),
TimeUnit.MILLISECONDS.toMinutes(milliSeconds) - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(milliSeconds)),
TimeUnit.MILLISECONDS.toSeconds(milliSeconds) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(milliSeconds)));
return hms;
}
}
2)activity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBackground">
<ProgressBar
android:id="@+id/progress_bar"
android:animationResolution="6000"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="14dp"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:max="100"
android:progress="0"
android:progressDrawable="@drawable/progressbar_vertical" />
<ProgressBar
android:id="@+id/progress_bar1"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="14dp"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:animationResolution="6000"
android:max="100"
android:progress="0"
android:progressDrawable="@drawable/progressbar_vertical" />
<EditText
android:id="@+id/editTextMinute"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/textViewTime"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:gravity="center"
android:hint="@string/hint_minute"
android:inputType="number"
android:maxLength="15"
android:maxLines="1"
android:minEms="5"
android:text=""
android:textColor="@color/colorYellow"
android:textColorHint="@color/colorYellow" />
<TextView
android:id="@+id/textViewTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="00:01:00"
android:textColor="@color/colorYellow"
android:textSize="40sp" />
<ImageView
android:id="@+id/imageViewStartStop"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_below="@+id/textViewTime"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:src="@drawable/icon_start" />
<ImageView
android:id="@+id/imageViewReset"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_below="@+id/imageViewStartStop"
android:layout_centerInParent="true"
android:layout_marginTop="30dp"
android:src="@drawable/icon_reset"
tools:visibility="visible"
android:visibility="gone" />
</RelativeLayout>
The following contains some screenshots please take a look.
The Above code works
Now i added service code that i am trying -ProgressBarService.java (In this i don't know which code should i move from main activity to this and HOW??)
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import static android.view.Gravity.END;
public class ProgressBarService extends Service {
private WindowManager mWindowManager;
private View mProgressView;
public ProgressBarService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@SuppressLint("InflateParams")
@Override
public void onCreate() {
super.onCreate();
mProgressView = LayoutInflater.from(this).inflate(R.layout.progressbar_view, null);
final WindowManager.LayoutParams paramsProgress = getLayoutParams();
paramsProgress.gravity=END;
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
if (mWindowManager != null) {
mWindowManager.addView(mProgressView, paramsProgress);
}
}
private WindowManager.LayoutParams getLayoutParams() {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
return params;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mProgressView != null) mWindowManager.removeView(mProgressView);
}
}
-progressbar_view (seperated from activity_main)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progress_bar"
android:animationResolution="6000"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="14dp"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:max="100"
android:progress="100"
android:progressDrawable="@drawable/progressbar_vertical" />
<!-- <ProgressBar
android:id="@+id/progress_bar1"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="14dp"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:animationResolution="6000"
android:max="100"
android:progress="100"
android:progressDrawable="@drawable/progressbar_vertical" />-->
</RelativeLayout>