diff --git a/app/build.gradle b/app/build.gradle index cd2310e..2416e85 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -197,6 +197,9 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:2.2.10" // 添加这行,使用 BOM 统一 Kotlin 相关库的版本 implementation(platform("org.jetbrains.kotlin:kotlin-bom:2.2.10")) + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.11.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-rx3:1.11.0' //保持1.3.1 更新会报错 implementation 'androidx.appcompat:appcompat:1.7.1' @@ -209,6 +212,7 @@ dependencies { // Java language implementation implementation "androidx.fragment:fragment:1.8.9" implementation "androidx.viewpager2:viewpager2:1.1.0" + implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' // Room依赖 implementation "androidx.room:room-runtime:2.8.4" diff --git a/app/src/main/java/com/ttstd/dialer/activity/main/MainActivity.java b/app/src/main/java/com/ttstd/dialer/activity/main/MainActivity.java index f7fe0a9..a1465f4 100644 --- a/app/src/main/java/com/ttstd/dialer/activity/main/MainActivity.java +++ b/app/src/main/java/com/ttstd/dialer/activity/main/MainActivity.java @@ -5,14 +5,18 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.view.KeyEvent; +import android.view.Window; +import android.view.WindowManager; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.lifecycle.Observer; +import androidx.loader.app.LoaderManager; import androidx.viewpager.widget.ViewPager; import com.tencent.mmkv.MMKV; import com.ttstd.dialer.R; +import com.ttstd.dialer.adapter.AppGridAdapter; import com.ttstd.dialer.base.mvvm.BaseMvvmActivity; import com.ttstd.dialer.config.CommonConfig; import com.ttstd.dialer.databinding.ActivityMainBinding; @@ -21,16 +25,25 @@ import com.ttstd.dialer.fragment.app.AppFragment; import com.ttstd.dialer.fragment.contact.ContactFragment; import com.ttstd.dialer.fragment.home.HomeFragment; import com.ttstd.dialer.fragment.settings.SettingsFragment; +import com.ttstd.dialer.manager.AppManager; +import com.ttstd.dialer.manager.WeatherUpdateManager; import com.ttstd.dialer.service.main.MainService; import com.ttstd.dialer.utils.Logger; import com.ttstd.dialer.view.ApkPagerAdapter; import com.ttstd.dialer.view.ScaleCircleNavigator; +import com.ttstd.dialer.wallpager.WallpaperScrollHelper; import net.lucode.hackware.magicindicator.ViewPagerHelper; +import java.util.concurrent.CancellationException; import java.util.ArrayList; import java.util.List; +import kotlinx.coroutines.CoroutineScope; +import kotlinx.coroutines.CoroutineScopeKt; +import kotlinx.coroutines.Job; +import kotlinx.coroutines.flow.FlowKt; + public class MainActivity extends BaseMvvmActivity { private static final String TAG = "MainActivity"; private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); @@ -38,7 +51,7 @@ public class MainActivity extends BaseMvvmActivity mFragments = new ArrayList<>(); private SettingsFragment mSettingsFragment; private ContactFragment mContactFragment; @@ -48,12 +61,17 @@ public class MainActivity extends BaseMvvmActivity mAppInfos; + private WallpaperScrollHelper wallpaperScrollHelper; + + private WeatherUpdateManager weatherUpdateManager; + private final CoroutineScope weatherScope = CoroutineScopeKt.MainScope(); + private Job weatherNowJob; + private Job weatherHourlyJob; + private Job weatherDailyJob; @Override public boolean setNightMode() { @@ -76,11 +94,18 @@ public class MainActivity extends BaseMvvmActivity>() { @Override public void onChanged(List appInfos) { @@ -169,6 +222,16 @@ public class MainActivity extends BaseMvvmActivity>() { + @Override + public void onChanged(List appInfos) { + mViewDataBinding.gvApp.setNumColumns(appInfos.size()); + mAppGridAdapter.setAppInfos(appInfos); + } + }); + mViewModel.getHotseatApp(); + } @Override @@ -189,6 +252,9 @@ public class MainActivity extends BaseMvvmActivity { + if (weatherNowResponse != null) { + Logger.e(TAG, "weatherNowResponse = " + weatherNowResponse); + } + return null; + }); + } + + private void showSystemWallpaperBehindActivity() { + Window window = getWindow(); + + // 关键:让系统壁纸显示在当前窗口后面 + window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER); + + // 窗口背景透明 + window.setBackgroundDrawableResource(android.R.color.transparent); } private void registerReceivers() { @@ -231,6 +330,7 @@ public class MainActivity extends BaseMvvmActivity appInfos) { + Logger.e("getHotseatApp", "onNext: " + appInfos); + mHotseatAppData.setValue(appInfos); + } + + @Override + public void onError(@NonNull Throwable e) { + Logger.e("getHotseatApp", "onError: " + e.getMessage()); + } + + @Override + public void onComplete() { + Logger.e("getHotseatApp", "onComplete: "); + } + }); + } + } diff --git a/app/src/main/java/com/ttstd/dialer/adapter/AppGridAdapter.java b/app/src/main/java/com/ttstd/dialer/adapter/AppGridAdapter.java new file mode 100644 index 0000000..bb2ab4f --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/adapter/AppGridAdapter.java @@ -0,0 +1,162 @@ +package com.ttstd.dialer.adapter; + +import android.content.ComponentName; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.fragment.app.FragmentActivity; +import androidx.loader.app.LoaderManager; +import androidx.loader.content.Loader; + +import com.shehuan.niv.NiceImageView; +import com.tencent.mmkv.MMKV; +import com.ttstd.dialer.R; +import com.ttstd.dialer.config.CommonConfig; +import com.ttstd.dialer.db.app.AppInfo; +import com.ttstd.dialer.fragment.dialog.shortcut.ShortcutDialogFagment; +import com.ttstd.dialer.utils.ApkUtils; +import com.ttstd.dialer.utils.Logger; +import com.ttstd.iconloader.IconCacheManager; +import com.ttstd.iconloader.IconLoader; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class AppGridAdapter extends BaseAdapter implements LoaderManager.LoaderCallbacks { + private static final String TAG = "AppGridAdapter"; + + private MMKV mMMKV = MMKV.mmkvWithID(CommonConfig.MMKV_ID, MMKV.MULTI_PROCESS_MODE); + + private FragmentActivity mContext; + private LoaderManager mLoaderManager; + private IconCacheManager mIconCacheManager = IconCacheManager.getInstance(); + private List mAppInfos; + + public AppGridAdapter(FragmentActivity context, LoaderManager loaderManager) { + mContext = context; + mLoaderManager = loaderManager; + } + + public void setAppInfos(List appInfos) { + mAppInfos = appInfos; + notifyDataSetChanged(); + } + + public interface ShortcutCallback { + void setAppInside(AppInfo appInfo); + } + + private ShortcutCallback mShortcutCallback; + + public void setShortcutCallback(ShortcutCallback shortcutCallback) { + mShortcutCallback = shortcutCallback; + } + + @Override + public int getCount() { + return mAppInfos == null ? 0 : mAppInfos.size(); + } + + @Override + public Object getItem(int position) { + return mAppInfos == null ? null : mAppInfos.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + if (convertView == null) { + convertView = LayoutInflater.from(mContext).inflate(R.layout.item_grid_app, parent, false); + holder = new ViewHolder(); + holder.root = convertView.findViewById(R.id.root); + holder.iv_icon = convertView.findViewById(R.id.iv_icon); + holder.tv_app_name = convertView.findViewById(R.id.tv_app_name); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + AppInfo appInfo = mAppInfos.get(position); + Drawable drawable = mIconCacheManager.getIcon(appInfo.getComponentName().flattenToShortString()); + if (drawable != null) { + holder.iv_icon.setImageDrawable(drawable); + } else { + mLoaderManager.restartLoader(position, null, this).forceLoad(); + } + holder.tv_app_name.setText(appInfo.getLabel()); + + holder.root.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ApkUtils.openApp(mContext, appInfo.getComponentName()); + } + }); + + holder.root.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + ShortcutDialogFagment shortcutDialogFagment = new ShortcutDialogFagment(appInfo); + shortcutDialogFagment.setTitil("温馨提示"); + shortcutDialogFagment.setTips("是否将应用放入更多应用"); + shortcutDialogFagment.setOnClickListener(new ShortcutDialogFagment.OnClickListener() { + @Override + public void onPositiveClick() { + if (mShortcutCallback != null) + mShortcutCallback.setAppInside(appInfo); + shortcutDialogFagment.dismiss(); + } + + @Override + public void onNegativeClick() { + shortcutDialogFagment.dismiss(); + } + }); + shortcutDialogFagment.show(mContext.getSupportFragmentManager(), "ShortcutDialogFagment"); + return false; + } + }); + + return convertView; + } + + @NonNull + @NotNull + @Override + public Loader onCreateLoader(int id, @Nullable @org.jetbrains.annotations.Nullable Bundle args) { + AppInfo appInfo = mAppInfos.get(id); + ComponentName componentName = appInfo.getComponentName(); + return new IconLoader(mContext, componentName, mIconCacheManager); + } + + @Override + public void onLoadFinished(@NonNull @NotNull Loader loader, Drawable data) { + int position = loader.getId(); + notifyDataSetChanged(); + } + + @Override + public void onLoaderReset(@NonNull @NotNull Loader loader) { + Logger.e(TAG, "onLoaderReset: "); + } + + static class ViewHolder { + ConstraintLayout root; + NiceImageView iv_icon; + TextView tv_app_name; + } +} diff --git a/app/src/main/java/com/ttstd/dialer/config/CommonConfig.java b/app/src/main/java/com/ttstd/dialer/config/CommonConfig.java index 118a034..0090ccb 100644 --- a/app/src/main/java/com/ttstd/dialer/config/CommonConfig.java +++ b/app/src/main/java/com/ttstd/dialer/config/CommonConfig.java @@ -10,6 +10,12 @@ public class CommonConfig { public static final String FLOAT_WINDOW_ENABLE = "float_window_enable_key"; public static final String FLOAT_WINDOW_KILL_APP = "float_window_kill_app_key"; + public static final String FRAGMENT_APP_ROW_KEY = "fragment_app_row_key"; + public static final int FRAGMENT_APP_ROW_DEFAULT_VALUE = 2; + public static final String FRAGMENT_APP_COLUMN_KEY = "fragment_app_column_key"; + public static final int FRAGMENT_APP_COLUMN_DEFAULT_VALUE = 3; + + public static final String WECHAT_AUTO_ACCEPT_CALL= "wechat_auto_accept_call_key"; public static final String WECHAT_AUTO_HNADS_FREE= "wechat_auto_hands_free_key"; diff --git a/app/src/main/java/com/ttstd/dialer/db/app/AppDao.java b/app/src/main/java/com/ttstd/dialer/db/app/AppDao.java index 134a10b..bbe06c3 100644 --- a/app/src/main/java/com/ttstd/dialer/db/app/AppDao.java +++ b/app/src/main/java/com/ttstd/dialer/db/app/AppDao.java @@ -52,6 +52,9 @@ public interface AppDao { @Query("SELECT * FROM app_list WHERE outside = 1 ORDER BY position ASC") List getOutsideApp(); + @Query("SELECT * FROM app_list WHERE outside = 1 AND hotseat = 0 ORDER BY position ASC") + List getOutsideAppWithoutHotseat(); + @Query("SELECT * FROM app_list WHERE outside = 0 ORDER BY position ASC") List getInsideApp(); @@ -60,4 +63,10 @@ public interface AppDao { @Query("SELECT * FROM app_list WHERE label LIKE :searchQuery OR package_name LIKE :searchQuery") List searchApp(String searchQuery); + + @Query("SELECT * FROM app_list WHERE hotseat = 1 ORDER BY hotseat_position ASC") + List getHotseatApp(); + + @Query("UPDATE app_list SET hotseat = :isHotseat WHERE id = :id") + int updateHotseatStatus(int id, int isHotseat); } diff --git a/app/src/main/java/com/ttstd/dialer/db/app/AppInfo.java b/app/src/main/java/com/ttstd/dialer/db/app/AppInfo.java index 23fb455..1c4173d 100644 --- a/app/src/main/java/com/ttstd/dialer/db/app/AppInfo.java +++ b/app/src/main/java/com/ttstd/dialer/db/app/AppInfo.java @@ -39,6 +39,10 @@ public class AppInfo implements Serializable , Parcelable { private int mPosition; @ColumnInfo(name = "outside") private int outside; + @ColumnInfo(name = "hotseat") + private int hotseat; + @ColumnInfo(name = "hotseat_position") + private int hotseatPosition; public AppInfo() { @@ -51,6 +55,8 @@ public class AppInfo implements Serializable , Parcelable { this.mClassName = componentName.getClassName(); this.mPosition = 0; this.outside = 0; + this.hotseat = 0; + this.hotseatPosition = 0; } public AppInfo(Context context, ComponentName componentName, int pos) { @@ -60,6 +66,8 @@ public class AppInfo implements Serializable , Parcelable { this.mClassName = componentName.getClassName(); this.mPosition = pos; this.outside = 0; + this.hotseat = 0; + this.hotseatPosition = 0; } protected AppInfo(Parcel in) { @@ -70,6 +78,7 @@ public class AppInfo implements Serializable , Parcelable { mClassName = in.readString(); mPosition = in.readInt(); outside = in.readInt(); + hotseatPosition = in.readInt(); } public static final Creator CREATOR = new Creator() { @@ -140,6 +149,22 @@ public class AppInfo implements Serializable , Parcelable { this.outside = outside; } + public int getHotseat() { + return hotseat; + } + + public void setHotseat(int hotseat) { + this.hotseat = hotseat; + } + + public int getHotseatPosition() { + return hotseatPosition; + } + + public void setHotseatPosition(int hotseatPosition) { + this.hotseatPosition = hotseatPosition; + } + @Override public boolean equals(@Nullable @org.jetbrains.annotations.Nullable Object obj) { if (obj instanceof AppInfo) { @@ -177,5 +202,7 @@ public class AppInfo implements Serializable , Parcelable { dest.writeString(mClassName); dest.writeInt(mPosition); dest.writeInt(outside); + dest.writeInt(hotseat); + dest.writeInt(hotseatPosition); } } diff --git a/app/src/main/java/com/ttstd/dialer/db/app/AppRepository.java b/app/src/main/java/com/ttstd/dialer/db/app/AppRepository.java index 71d52cf..fdc822b 100644 --- a/app/src/main/java/com/ttstd/dialer/db/app/AppRepository.java +++ b/app/src/main/java/com/ttstd/dialer/db/app/AppRepository.java @@ -37,11 +37,21 @@ public class AppRepository { return mAppDao.getAllApp(); } + // 获取快捷方式APP + public List getHotseatApp() { + return mAppDao.getHotseatApp(); + } + // 获取在外面显示的APP public List getOutsideApp() { return mAppDao.getOutsideApp(); } + // 获取外面显示的APP,不包含快捷方式APP + public List getOutsideAppWithoutHotseat() { + return mAppDao.getOutsideAppWithoutHotseat(); + } + // 获取在里面显示的APP public List getInsideApp() { return mAppDao.getInsideApp(); diff --git a/app/src/main/java/com/ttstd/dialer/fragment/app/AppFragment.java b/app/src/main/java/com/ttstd/dialer/fragment/app/AppFragment.java index 74f9bf7..75e61c8 100644 --- a/app/src/main/java/com/ttstd/dialer/fragment/app/AppFragment.java +++ b/app/src/main/java/com/ttstd/dialer/fragment/app/AppFragment.java @@ -10,10 +10,11 @@ import androidx.recyclerview.widget.GridLayoutManager; import com.ttstd.dialer.R; import com.ttstd.dialer.adapter.AppAdapter; import com.ttstd.dialer.base.mvvm.fragment.BaseMvvmFragment; +import com.ttstd.dialer.config.CommonConfig; import com.ttstd.dialer.databinding.FragmentAppBinding; import com.ttstd.dialer.db.app.AppInfo; import com.ttstd.dialer.utils.Logger; -import com.ttstd.dialer.view.EqualHeightDecoration; +import com.ttstd.dialer.view.EqualSpaceDecoration; import java.util.List; @@ -81,8 +82,8 @@ public class AppFragment extends BaseMvvmFragment DEFAULT_DIALER_PACKAGE = new HashSet() {{ + this.add("com.android.dialer"); + }}; + + public static final Set DEFAULT_CONTACT_PACKAGE = new HashSet() {{ + this.add("com.android.contacts"); + }}; + + public static final Set DEFAULT_MESSAGE_PACKAGE = new HashSet() {{ + this.add("com.android.messaging"); + }}; + + public static final Set DEFAULT_SETTINGS_PACKAGE = new HashSet() {{ + this.add("com.android.settings"); + }}; + + public static void init(Context context) { if (INSTANCE == null) { synchronized (AppManager.class) { @@ -197,6 +214,7 @@ public class AppManager { .sorted(getAppComparator()) .collect(Collectors.toList()); + final int[] hotseatCounter = {0}; IntStream.range(0, allFirstApps.size()) .forEach(new IntConsumer() { @Override @@ -207,6 +225,24 @@ public class AppManager { } else { appInfo.setOutside(0); } + + if (DEFAULT_DIALER_PACKAGE.contains(appInfo.getPackageName())) { + appInfo.setHotseat(1); + appInfo.setHotseatPosition(hotseatCounter[0]++); + } else if (DEFAULT_CONTACT_PACKAGE.contains(appInfo.getPackageName())) { + appInfo.setHotseat(1); + appInfo.setHotseatPosition(hotseatCounter[0]++); + } else if (DEFAULT_MESSAGE_PACKAGE.contains(appInfo.getPackageName())) { + appInfo.setHotseat(1); + appInfo.setHotseatPosition(hotseatCounter[0]++); + } else if (DEFAULT_SETTINGS_PACKAGE.contains(appInfo.getPackageName())) { + appInfo.setHotseat(1); + appInfo.setHotseatPosition(hotseatCounter[0]++); + } else { + appInfo.setHotseat(0); + appInfo.setHotseatPosition(0); + } + appInfo.setPosition(index); } }); diff --git a/app/src/main/java/com/ttstd/dialer/manager/WeatherManager.java b/app/src/main/java/com/ttstd/dialer/manager/WeatherManager.java index bfef50a..46fbf9b 100644 --- a/app/src/main/java/com/ttstd/dialer/manager/WeatherManager.java +++ b/app/src/main/java/com/ttstd/dialer/manager/WeatherManager.java @@ -11,6 +11,7 @@ import com.qweather.sdk.QWeather; import com.qweather.sdk.basic.Lang; import com.qweather.sdk.basic.Unit; import com.qweather.sdk.parameter.weather.WeatherParameter; +import com.qweather.sdk.response.error.ErrorResponse; import com.qweather.sdk.response.weather.WeatherDailyResponse; import com.qweather.sdk.response.weather.WeatherHourlyResponse; import com.qweather.sdk.response.weather.WeatherNowResponse; @@ -33,6 +34,10 @@ import io.reactivex.rxjava3.core.ObservableOnSubscribe; import io.reactivex.rxjava3.core.Observer; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; +import kotlinx.coroutines.CoroutineScope; +import kotlinx.coroutines.CoroutineStart; +import kotlinx.coroutines.Dispatchers; +import kotlinx.coroutines.GlobalScope; public class WeatherManager { private static final String TAG = "WeatherManager"; @@ -44,10 +49,11 @@ public class WeatherManager { private Map LocationIDMap = new HashMap<>(); private boolean loadCsvFinish = false; private String mAdCode; - + private WeatherUpdateManager weatherUpdateManager; private WeatherManager(Context context) { this.mContext = context.getApplicationContext(); + weatherUpdateManager = WeatherUpdateManager.Companion.getInstance(); initCsv(); try { // 通过SDK提供的JWTGenerator设置令牌生成器,其实现自TokenGenerator接口 @@ -155,7 +161,30 @@ public class WeatherManager { WeatherParameter parameter = new WeatherParameter(locationID) .lang(Lang.ZH_HANS) .unit(Unit.METRIC); - mQWeather.weatherNow(parameter, callback); + mQWeather.weatherNow(parameter, new Callback() { + @Override + public void onSuccess(WeatherNowResponse weatherNowResponse) { + Logger.e(TAG, "getWeatherNow", "onSuccess: " + weatherNowResponse); + weatherUpdateManager.publishWeatherNowUpdate(weatherNowResponse); + if (callback != null) { + callback.onSuccess(weatherNowResponse); + } + } + + @Override + public void onFailure(ErrorResponse errorResponse) { + if (callback != null) { + callback.onFailure(errorResponse); + } + } + + @Override + public void onException(Throwable throwable) { + if (callback != null) { + callback.onException(throwable); + } + } + }); } public void getWeather24h(String adCode, Callback callback) { @@ -164,7 +193,30 @@ public class WeatherManager { WeatherParameter parameter = new WeatherParameter(locationID) .lang(Lang.ZH_HANS) .unit(Unit.METRIC); - mQWeather.weather24h(parameter, callback); + mQWeather.weather24h(parameter, new Callback() { + @Override + public void onSuccess(WeatherHourlyResponse weatherHourlyResponse) { + Logger.e(TAG, "getWeather24h", "onSuccess: " + weatherHourlyResponse); + weatherUpdateManager.publishWeatherHourlyUpdate(weatherHourlyResponse); + if (callback != null) { + callback.onSuccess(weatherHourlyResponse); + } + } + + @Override + public void onFailure(ErrorResponse errorResponse) { + if (callback != null) { + callback.onFailure(errorResponse); + } + } + + @Override + public void onException(Throwable throwable) { + if (callback != null) { + callback.onException(throwable); + } + } + }); } public void getWeather10D(String adCode, Callback callback) { @@ -173,7 +225,30 @@ public class WeatherManager { WeatherParameter parameter = new WeatherParameter(locationID) .lang(Lang.ZH_HANS) .unit(Unit.METRIC); - mQWeather.weather10d(parameter, callback); + mQWeather.weather10d(parameter, new Callback() { + @Override + public void onSuccess(WeatherDailyResponse weatherDailyResponse) { + Logger.e(TAG, "getWeather10D", "onSuccess: " + weatherDailyResponse); + weatherUpdateManager.publishWeatherDailyUpdate(weatherDailyResponse); + if (callback != null) { + callback.onSuccess(weatherDailyResponse); + } + } + + @Override + public void onFailure(ErrorResponse errorResponse) { + if (callback != null) { + callback.onFailure(errorResponse); + } + } + + @Override + public void onException(Throwable throwable) { + if (callback != null) { + callback.onException(throwable); + } + } + }); } diff --git a/app/src/main/java/com/ttstd/dialer/manager/WeatherUpdateManager.kt b/app/src/main/java/com/ttstd/dialer/manager/WeatherUpdateManager.kt new file mode 100644 index 0000000..65c1568 --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/manager/WeatherUpdateManager.kt @@ -0,0 +1,104 @@ +package com.ttstd.dialer.manager + +import com.qweather.sdk.response.weather.WeatherDailyResponse +import com.qweather.sdk.response.weather.WeatherHourlyResponse +import com.qweather.sdk.response.weather.WeatherNowResponse +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch + +/** + * 天气数据更新管理器 + * 使用 Kotlin Flow 实现观察者模式,替代 LiveEventBus + */ +class WeatherUpdateManager { + + companion object { + @Volatile + private var INSTANCE: WeatherUpdateManager? = null + + fun getInstance(): WeatherUpdateManager { + return INSTANCE ?: synchronized(this) { + INSTANCE ?: WeatherUpdateManager().also { INSTANCE = it } + } + } + } + + // 创建一个共享的协程作用域 + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + + // 用于发布天气数据更新的 MutableSharedFlow + private val _weatherNowUpdates = MutableSharedFlow(replay = 1) + private val _weatherHourlyUpdates = MutableSharedFlow(replay = 1) + private val _weatherDailyUpdates = MutableSharedFlow(replay = 1) + + // 对外暴露的只读 Flow + val weatherNowUpdates: SharedFlow = _weatherNowUpdates.asSharedFlow() + val weatherHourlyUpdates: SharedFlow = + _weatherHourlyUpdates.asSharedFlow() + val weatherDailyUpdates: SharedFlow = _weatherDailyUpdates.asSharedFlow() + + /** + * 发布当前天气更新事件 + * @param weatherData 当前天气数据 + */ + fun publishWeatherNowUpdate(weatherData: WeatherNowResponse?) { + scope.launch { + _weatherNowUpdates.emit(weatherData) + } + } + + /** + * 发布逐小时天气更新事件 + * @param weatherData 逐小时天气数据 + */ + fun publishWeatherHourlyUpdate(weatherData: WeatherHourlyResponse?) { + scope.launch { + _weatherHourlyUpdates.emit(weatherData) + } + } + + /** + * 发布每日天气更新事件 + * @param weatherData 每日天气数据 + */ + fun publishWeatherDailyUpdate(weatherData: WeatherDailyResponse?) { + scope.launch { + _weatherDailyUpdates.emit(weatherData) + } + } + + fun observeWeatherNow( + scope: CoroutineScope, + observer: (WeatherNowResponse?) -> Unit + ): Job { + return weatherNowUpdates + .onEach { observer(it) } + .launchIn(scope) + } + + fun observeWeatherHourly( + scope: CoroutineScope, + observer: (WeatherHourlyResponse?) -> Unit + ): Job { + return weatherHourlyUpdates + .onEach { observer(it) } + .launchIn(scope) + } + + fun observeWeatherDaily( + scope: CoroutineScope, + observer: (WeatherDailyResponse?) -> Unit + ): Job { + return weatherDailyUpdates + .onEach { observer(it) } + .launchIn(scope) + } +} diff --git a/app/src/main/java/com/ttstd/dialer/view/EqualHeightDecoration.java b/app/src/main/java/com/ttstd/dialer/view/EqualHeightDecoration.java deleted file mode 100644 index 06f74bf..0000000 --- a/app/src/main/java/com/ttstd/dialer/view/EqualHeightDecoration.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.ttstd.dialer.view; - -import android.graphics.Rect; -import android.view.View; -import android.view.ViewGroup; - -import androidx.recyclerview.widget.RecyclerView; - -import com.ttstd.dialer.utils.ScreenUtils; - -public class EqualHeightDecoration extends RecyclerView.ItemDecoration { - private int rowCount; - - public EqualHeightDecoration(int rowCount) { - this.rowCount = rowCount; - } - - @Override - public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { - int recyclerViewHeight = parent.getHeight(); - int height = recyclerViewHeight - 6 * ScreenUtils.dp2px(view.getResources(), 4); - int itemHeight = recyclerViewHeight / rowCount; - ViewGroup.LayoutParams params = view.getLayoutParams(); - params.height = itemHeight; - view.setLayoutParams(params); - } -} - diff --git a/app/src/main/java/com/ttstd/dialer/view/EqualSpaceDecoration.java b/app/src/main/java/com/ttstd/dialer/view/EqualSpaceDecoration.java new file mode 100644 index 0000000..10da2d7 --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/view/EqualSpaceDecoration.java @@ -0,0 +1,90 @@ +package com.ttstd.dialer.view; + +import android.graphics.Rect; +import android.view.View; +import android.view.ViewGroup; + +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.ttstd.dialer.utils.ScreenUtils; + +public class EqualSpaceDecoration extends RecyclerView.ItemDecoration { + private int rowCount; + private int spacing; + private Integer cachedItemWidth = null; + private Integer cachedItemHeight = null; + + public EqualSpaceDecoration(int rowCount, int spacing) { + this.rowCount = rowCount; + this.spacing = spacing; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { + int recyclerViewWidth = parent.getWidth(); + int recyclerViewHeight = parent.getHeight(); + + int dpSpacing = ScreenUtils.dp2px(view.getResources(), spacing); + + // 获取实际的列数 + int actualColumnCount = 2; // 默认2列 + if (parent.getLayoutManager() instanceof GridLayoutManager) { + GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager(); + actualColumnCount = layoutManager.getSpanCount(); + } + + // 计算总item数量 + int totalItemCount = parent.getAdapter() != null ? parent.getAdapter().getItemCount() : 0; + if (totalItemCount == 0) { + outRect.set(0, 0, 0, 0); + return; + } + + // 计算item的宽度 + // recyclerView宽度 - 左右固定间距(2*spacing) - 中间列间距((columnCount-1)*spacing) / 列数 + int totalHorizontalSpacing = 2 * dpSpacing + (actualColumnCount - 1) * dpSpacing; + int itemWidth = (recyclerViewWidth - totalHorizontalSpacing) / actualColumnCount; + + // 计算item的高度 + // recyclerView高度 - 上下固定间距(2*spacing) - 中间行间距((rowCount-1)*spacing) / 行数 + int totalVerticalSpacing = 2 * dpSpacing + (rowCount - 1) * dpSpacing; + int itemHeight = (recyclerViewHeight - totalVerticalSpacing) / rowCount; + + // 缓存计算的尺寸,避免重复计算 + if (cachedItemWidth == null || cachedItemHeight == null) { + cachedItemWidth = itemWidth; + cachedItemHeight = itemHeight; + } + + // 设置item的尺寸 + ViewGroup.LayoutParams params = view.getLayoutParams(); + params.width = cachedItemWidth; + params.height = cachedItemHeight; + view.setLayoutParams(params); + + // 获取当前item的位置 + int position = parent.getChildAdapterPosition(view); + if (position == RecyclerView.NO_POSITION) { + return; + } + + // 计算item所在的行和列 + int row = position / actualColumnCount; + int column = position % actualColumnCount; + + // 计算左边距:只有第一列设置spacing,其他列不设置 + int left = column == 0 ? dpSpacing : 0; + + // 计算上边距:只有第一行设置spacing,其他行不设置 + int top = row == 0 ? dpSpacing : 0; + + // 计算右边距:只有最后一列设置spacing,其他列不设置 + int right = column == actualColumnCount - 1 ? dpSpacing : 0; + + // 计算下边距:只有最后一行设置spacing,其他行不设置 + int bottom = row == rowCount - 1 ? dpSpacing : 0; + + outRect.set(left, top, right, bottom); + } +} diff --git a/app/src/main/java/com/ttstd/dialer/view/EquallyDividedItemDecoration.java b/app/src/main/java/com/ttstd/dialer/view/EquallyDividedItemDecoration.java new file mode 100644 index 0000000..141f57e --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/view/EquallyDividedItemDecoration.java @@ -0,0 +1,74 @@ +package com.ttstd.dialer.view; + +import android.graphics.Rect; +import android.util.Log; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +public class EquallyDividedItemDecoration extends RecyclerView.ItemDecoration { + private static final String TAG = "EquallyDividedItemDecoration"; + + private int mSpanCount;// 横条目数量 + private int mHalfRowSpacing;// 行间距的一半 + private int mHalfColumnSpacing;// 列间距的一半 + + public EquallyDividedItemDecoration(int spanCount, int halfRowSpacing) { + mSpanCount = spanCount; + mHalfRowSpacing = halfRowSpacing; + mHalfColumnSpacing = halfRowSpacing; + } + + public EquallyDividedItemDecoration(int spanCount, int halfRowSpacing, int halfColumnSpacing) { + mSpanCount = spanCount; + mHalfRowSpacing = halfRowSpacing; + mHalfColumnSpacing = halfColumnSpacing; + } + + @Override + public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, + @NonNull RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + int position = parent.getChildAdapterPosition(view); // 获取view 在adapter中的位置。 + Log.d(TAG, "getItemOffsets: position = " + position); + + int itemCount = parent.getAdapter().getItemCount();// item全部数量 + Log.d(TAG, "getItemOffsets: itemCount = " + itemCount); + + int column = position % mSpanCount; // view 所在的列 + Log.d(TAG, "getItemOffsets: column = " + column); + + if (column == 0) { + outRect.left = 2 * mHalfRowSpacing; + outRect.right = mHalfRowSpacing; + } else if (column == mSpanCount - 1) { + outRect.left = mHalfRowSpacing; + outRect.right = 2 * mHalfRowSpacing; + } else { + outRect.left = mHalfRowSpacing; + outRect.right = mHalfRowSpacing; + } + + int row = (position / mSpanCount);// 所在行 + Log.d(TAG, "getItemOffsets: row = " + row); + int maxRow = (int) Math.ceil((double) itemCount / mSpanCount);// 一共多少行 + Log.d(TAG, "getItemOffsets: maxRow = " + maxRow); + + if (row == 0) { + outRect.top = 2 * mHalfColumnSpacing; + outRect.bottom = mHalfColumnSpacing; + } else if (row == maxRow - 1) { + outRect.top = mHalfColumnSpacing; + outRect.bottom = 2 * mHalfColumnSpacing; + } else { + outRect.top = mHalfColumnSpacing; + outRect.bottom = mHalfColumnSpacing; + } + + Log.d(TAG, "getItemOffsets: outRect.left = " + outRect.left); + Log.d(TAG, "getItemOffsets: outRect.right = " + outRect.right); + Log.d(TAG, "getItemOffsets: outRect.top = " + outRect.top); + Log.d(TAG, "getItemOffsets: outRect.bottom = " + outRect.bottom); + } +} diff --git a/app/src/main/java/com/ttstd/dialer/view/GridSpaceItemDecoration.java b/app/src/main/java/com/ttstd/dialer/view/GridSpaceItemDecoration.java new file mode 100644 index 0000000..148fa45 --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/view/GridSpaceItemDecoration.java @@ -0,0 +1,59 @@ +package com.ttstd.dialer.view; + +import android.graphics.Rect; +import android.util.Log; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +/** + * 描述 : RecyclerView GridLayoutManager 等间距。 + *

+ * 等间距需满足两个条件: + * 1.各个模块的大小相等,即 各列的left+right 值相等; + * 2.各列的间距相等,即 前列的right + 后列的left = 列间距; + *

+ * 在{@link #getItemOffsets(Rect, View, RecyclerView, RecyclerView.State)} 中针对 outRect 的left 和right 满足这两个条件即可 + *

+ * 作者 : shiguotao + * 版本 : V1 + * 创建时间 : 2020/3/19 4:54 PM + */ +public class GridSpaceItemDecoration extends RecyclerView.ItemDecoration { + + private final String TAG = "GridSpaceItemDecoration"; + + private int mSpanCount;//横条目数量 + private int mRowSpacing;//行间距 + private int mColumnSpacing;// 列间距 + + /** + * @param spanCount 列数 + * @param rowSpacing 行间距 + * @param columnSpacing 列间距 + */ + public GridSpaceItemDecoration(int spanCount, int rowSpacing, int columnSpacing) { + this.mSpanCount = spanCount; + this.mRowSpacing = rowSpacing; + this.mColumnSpacing = columnSpacing; + } + + @Override + public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { + int position = parent.getChildAdapterPosition(view); // 获取view 在adapter中的位置。 + int column = position % mSpanCount; // view 所在的列 + + outRect.left = column * mColumnSpacing / mSpanCount; // column * (列间距 * (1f / 列数)) + outRect.right = mColumnSpacing - (column + 1) * mColumnSpacing / mSpanCount; // 列间距 - (column + 1) * (列间距 * (1f /列数)) + + Log.e(TAG, "position:" + position + + " columnIndex: " + column + + " left,right ->" + outRect.left + "," + outRect.right); + + // 如果position > 行数,说明不是在第一行,则不指定行高,其他行的上间距为 top=mRowSpacing + if (position >= mSpanCount) { + outRect.top = mRowSpacing; // item top + } + } +} diff --git a/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperBackgroundView.java b/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperBackgroundView.java new file mode 100644 index 0000000..fe6e32c --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperBackgroundView.java @@ -0,0 +1,77 @@ +package com.ttstd.dialer.wallpager; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + +public class WallpaperBackgroundView extends View { + + private Bitmap wallpaperBitmap; + private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + + private float progress = 0f; + + public WallpaperBackgroundView(Context context) { + super(context); + } + + public WallpaperBackgroundView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public void setWallpaperBitmap(Bitmap bitmap) { + this.wallpaperBitmap = bitmap; + invalidate(); + } + + /** + * @param progress 0 ~ 1 + */ + public void setScrollProgress(float progress) { + this.progress = Math.max(0f, Math.min(1f, progress)); + invalidate(); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (wallpaperBitmap == null || wallpaperBitmap.isRecycled()) { + return; + } + + int viewWidth = getWidth(); + int viewHeight = getHeight(); + + int bitmapWidth = wallpaperBitmap.getWidth(); + int bitmapHeight = wallpaperBitmap.getHeight(); + + // 让图片铺满高度 + float scale = Math.max( + viewHeight * 1f / bitmapHeight, + viewWidth * 1f / bitmapWidth + ); + + float scaledWidth = bitmapWidth * scale; + float scaledHeight = bitmapHeight * scale; + + // 图片比屏幕宽出的部分,用于横向滑动 + float maxMoveX = Math.max(0, scaledWidth - viewWidth); + + float left = -maxMoveX * progress; + float top = (viewHeight - scaledHeight) / 2f; + + RectF dst = new RectF( + left, + top, + left + scaledWidth, + top + scaledHeight + ); + + canvas.drawBitmap(wallpaperBitmap, null, dst, paint); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperBitmapUtils.java b/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperBitmapUtils.java new file mode 100644 index 0000000..5b59c8e --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperBitmapUtils.java @@ -0,0 +1,57 @@ +package com.ttstd.dialer.wallpager; + +import android.app.WallpaperManager; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Build; + +public class WallpaperBitmapUtils { + + public static Bitmap getCurrentWallpaperBitmap(Context context) { + WallpaperManager manager = WallpaperManager.getInstance(context); + + try { + Drawable drawable; + + if (Build.VERSION.SDK_INT >= 34) { + // Android 14+ 普通 App 基本不要再用 getDrawable() + return null; + } else { + drawable = manager.getDrawable(); + } + + if (drawable == null) { + return null; + } + + if (drawable instanceof BitmapDrawable) { + return ((BitmapDrawable) drawable).getBitmap(); + } + + int width = Math.max(1, drawable.getIntrinsicWidth()); + int height = Math.max(1, drawable.getIntrinsicHeight()); + + Bitmap bitmap = Bitmap.createBitmap( + width, + height, + Bitmap.Config.ARGB_8888 + ); + + Canvas canvas = new Canvas(bitmap); + drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); + drawable.draw(canvas); + + return bitmap; + + } catch (SecurityException e) { + e.printStackTrace(); + return null; + } catch (Throwable e) { + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperScrollHelper.java b/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperScrollHelper.java new file mode 100644 index 0000000..bf9a1a8 --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperScrollHelper.java @@ -0,0 +1,103 @@ +package com.ttstd.dialer.wallpager; + +import android.app.WallpaperManager; +import android.content.Context; +import android.os.IBinder; +import android.util.Log; +import android.view.View; + +public class WallpaperScrollHelper { + + private static final String TAG = "WallpaperScrollHelper"; + + private final Context context; + private final WallpaperManager wallpaperManager; + private final View windowView; + + private int pageCount; + + public WallpaperScrollHelper(Context context, View windowView, int pageCount) { + this.context = context.getApplicationContext(); + this.windowView = windowView; + this.wallpaperManager = WallpaperManager.getInstance(context); + this.pageCount = Math.max(1, pageCount); + } + + public void onResume() { + setupWallpaperOffsetSteps(); + syncWallpaperOffset(0f); + } + + public void onDestroy() { + // 可选:清理偏移 + try { + IBinder token = windowView.getWindowToken(); + if (token != null) { + wallpaperManager.clearWallpaperOffsets(token); + } + } catch (Throwable ignored) { + } + } + + public void setPageCount(int pageCount) { + this.pageCount = Math.max(1, pageCount); + setupWallpaperOffsetSteps(); + } + + private void setupWallpaperOffsetSteps() { + if (pageCount <= 1) { + wallpaperManager.setWallpaperOffsetSteps(1f, 1f); + return; + } + + float step = 1f / (pageCount - 1); + + try { + wallpaperManager.setWallpaperOffsetSteps(step, 1f); + } catch (Throwable e) { + Log.w(TAG, "setWallpaperOffsetSteps failed", e); + } + } + + /** + * @param scrollProgress 总滑动进度,范围 0 ~ 1 + * 第 1 页是 0 + * 最后一页是 1 + */ + public void syncWallpaperOffset(float scrollProgress) { + scrollProgress = clamp(scrollProgress, 0f, 1f); + + IBinder token = windowView.getWindowToken(); + if (token == null) { + return; + } + + try { + // yOffset 通常保持 0.5f + wallpaperManager.setWallpaperOffsets(token, scrollProgress, 0.5f); + } catch (IllegalArgumentException e) { + Log.w(TAG, "setWallpaperOffsets IllegalArgumentException", e); + } catch (Throwable e) { + Log.w(TAG, "setWallpaperOffsets failed", e); + } + } + + /** + * 根据 scrollX 转换成 0~1 的壁纸偏移。 + */ + public void syncWithScroll(int scrollX, int pageWidth) { + if (pageCount <= 1 || pageWidth <= 0) { + syncWallpaperOffset(0f); + return; + } + + int maxScrollX = pageWidth * (pageCount - 1); + float progress = scrollX * 1f / maxScrollX; + + syncWallpaperOffset(progress); + } + + private float clamp(float value, float min, float max) { + return Math.max(min, Math.min(max, value)); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperViewPager.java b/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperViewPager.java new file mode 100644 index 0000000..85660fa --- /dev/null +++ b/app/src/main/java/com/ttstd/dialer/wallpager/WallpaperViewPager.java @@ -0,0 +1,88 @@ +package com.ttstd.dialer.wallpager; + +import android.content.Context; +import android.graphics.Color; +import android.util.AttributeSet; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; + +public class WallpaperViewPager extends ViewPager { + + private int pageCount = 1; + private WallpaperScrollHelper wallpaperScrollHelper; + + private final OnPageChangeListener wallpaperSyncListener = new OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + syncWallpaper(position, positionOffset); + } + + @Override + public void onPageSelected(int position) { + } + + @Override + public void onPageScrollStateChanged(int state) { + } + }; + + public WallpaperViewPager(@NonNull Context context) { + this(context, null); + } + + public WallpaperViewPager(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + } + + private void init() { + setBackgroundColor(Color.TRANSPARENT); + addOnPageChangeListener(wallpaperSyncListener); + } + + public void setWallpaperScrollHelper(WallpaperScrollHelper helper) { + this.wallpaperScrollHelper = helper; + if (helper != null) { + helper.setPageCount(pageCount); + post(this::syncWallpaperFromCurrentItem); + } + } + + public void setPageCount(int count) { + pageCount = Math.max(1, count); + if (wallpaperScrollHelper != null) { + wallpaperScrollHelper.setPageCount(pageCount); + post(this::syncWallpaperFromCurrentItem); + } + } + + @Override + public void setCurrentItem(int item) { + super.setCurrentItem(item); + syncWallpaperFromCurrentItem(); + } + + @Override + public void setCurrentItem(int item, boolean smoothScroll) { + super.setCurrentItem(item, smoothScroll); + syncWallpaperFromCurrentItem(); + } + + private void syncWallpaper(int position, float positionOffset) { + if (wallpaperScrollHelper == null) { + return; + } + if (pageCount <= 1) { + wallpaperScrollHelper.syncWallpaperOffset(0f); + return; + } + float progress = (position + positionOffset) / (pageCount - 1f); + wallpaperScrollHelper.syncWallpaperOffset(progress); + } + + private void syncWallpaperFromCurrentItem() { + syncWallpaper(getCurrentItem(), 0f); + } +} diff --git a/app/src/main/res/drawable-xxxhdpi/main_wallpaper.png b/app/src/main/res/drawable-xxxhdpi/main_wallpaper.png new file mode 100644 index 0000000..5202f2b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/main_wallpaper.png differ diff --git a/app/src/main/res/drawable/main_hotseat_background.xml b/app/src/main/res/drawable/main_hotseat_background.xml new file mode 100644 index 0000000..c8c10ab --- /dev/null +++ b/app/src/main/res/drawable/main_hotseat_background.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 30bb9c7..aa303f9 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -13,10 +13,9 @@ - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index ae0bbea..b0b3b97 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -24,213 +24,222 @@ android:layout_height="match_parent" android:orientation="vertical"> - + app:cardBackgroundColor="@color/card_background_color" + app:cardCornerRadius="@dimen/card_radius" + app:cardUseCompatPadding="true"> - + android:orientation="horizontal"> + android:layout_height="match_parent" + android:layout_weight="1"> - + app:layout_constraintTop_toTopOf="parent"> - + - + - + + + + + - - - - + android:layout_height="match_parent" + android:layout_weight="1"> - + app:layout_constraintTop_toTopOf="parent"> - + + + - + - + + + - + - + - + app:cardBackgroundColor="@color/card_background_color" + app:cardCornerRadius="@dimen/card_radius" + app:cardUseCompatPadding="true"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:onClick="@{click::openContact}" + android:orientation="horizontal"> - - - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + + + + + - + - + app:cardBackgroundColor="@color/card_background_color" + app:cardCornerRadius="@dimen/card_radius" + app:cardUseCompatPadding="true"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:onClick="@{click::openSettings}" + android:orientation="horizontal"> - - - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + + + + + - + @@ -335,97 +362,114 @@ android:layout_weight="1" android:orientation="horizontal"> - + app:cardBackgroundColor="@color/card_background_color" + app:cardCornerRadius="@dimen/card_radius" + app:cardUseCompatPadding="true"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:onClick="@{click::openDouyin}" + android:orientation="horizontal"> - - - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + + + + + + + - - - + app:cardBackgroundColor="@color/card_background_color" + app:cardCornerRadius="@dimen/card_radius" + app:cardUseCompatPadding="true"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:onClick="@{click::openWeixin}" + android:orientation="horizontal"> - - - + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + + + + + - - + diff --git a/app/src/main/res/layout/item_app.xml b/app/src/main/res/layout/item_app.xml index b89ced9..0a5c303 100644 --- a/app/src/main/res/layout/item_app.xml +++ b/app/src/main/res/layout/item_app.xml @@ -2,48 +2,62 @@ + android:layout_height="match_parent"> - + android:layout_height="match_parent" + app:cardBackgroundColor="@color/card_background_color" + app:cardCornerRadius="@dimen/card_radius" + app:cardUseCompatPadding="true"> - - - + app:layout_constraintTop_toTopOf="parent"> - + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_contact_home.xml b/app/src/main/res/layout/item_contact_home.xml index 013fc67..e7e1420 100644 --- a/app/src/main/res/layout/item_contact_home.xml +++ b/app/src/main/res/layout/item_contact_home.xml @@ -4,7 +4,15 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + + + app:layout_constraintVertical_bias="0.12" /> + app:layout_constraintVertical_bias="0.3"> - + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_grid_app.xml b/app/src/main/res/layout/item_grid_app.xml new file mode 100644 index 0000000..b870565 --- /dev/null +++ b/app/src/main/res/layout/item_grid_app.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-sw1024dp/dimens.xml b/app/src/main/res/values-sw1024dp/dimens.xml index 5e0d0de..bee23c7 100644 --- a/app/src/main/res/values-sw1024dp/dimens.xml +++ b/app/src/main/res/values-sw1024dp/dimens.xml @@ -2,6 +2,11 @@ 56.8889sp 22.7556dp + 227.5556dp + 51.2000sp + 42.6667sp + 5.6889dp + 45.5111dp -170.6667dp -85.3333dp -56.8889dp @@ -11,7 +16,6 @@ -14.2222dp -5.6889dp -2.8444dp - 0.0000dp 0.2844dp 1.4222dp 2.8444dp diff --git a/app/src/main/res/values-sw1280dp/dimens.xml b/app/src/main/res/values-sw1280dp/dimens.xml index 3760743..c21a0e3 100644 --- a/app/src/main/res/values-sw1280dp/dimens.xml +++ b/app/src/main/res/values-sw1280dp/dimens.xml @@ -2,7 +2,11 @@ 71.1111sp 28.4444dp - + 284.4444dp + 64.0000sp + 53.3333sp + 7.1111dp + 56.8889dp -213.3333dp -106.6667dp -71.1111dp @@ -12,7 +16,6 @@ -17.7778dp -7.1111dp -3.5556dp - 0.0000dp 0.3556dp 1.7778dp 3.5556dp diff --git a/app/src/main/res/values-sw1365dp/dimens.xml b/app/src/main/res/values-sw1365dp/dimens.xml index c273c6e..6d385bd 100644 --- a/app/src/main/res/values-sw1365dp/dimens.xml +++ b/app/src/main/res/values-sw1365dp/dimens.xml @@ -2,6 +2,11 @@ 75.8333sp 30.3333dp + 303.3333dp + 68.2500sp + 56.8750sp + 7.5833dp + 60.6667dp -227.5000dp -113.7500dp -75.8333dp @@ -11,7 +16,6 @@ -18.9583dp -7.5833dp -3.7917dp - 0.0000dp 0.3792dp 1.8958dp 3.7917dp diff --git a/app/src/main/res/values-sw240dp/dimens.xml b/app/src/main/res/values-sw240dp/dimens.xml index 4fd1887..d800cc0 100644 --- a/app/src/main/res/values-sw240dp/dimens.xml +++ b/app/src/main/res/values-sw240dp/dimens.xml @@ -2,6 +2,11 @@ 13.3333sp 5.3333dp + 53.3333dp + 12.0000sp + 10.0000sp + 1.3333dp + 10.6667dp -40.0000dp -20.0000dp -13.3333dp @@ -11,7 +16,6 @@ -3.3333dp -1.3333dp -0.6667dp - 0.0000dp 0.0667dp 0.3333dp 0.6667dp diff --git a/app/src/main/res/values-sw320dp/dimens.xml b/app/src/main/res/values-sw320dp/dimens.xml index 77a7242..a84dbc2 100644 --- a/app/src/main/res/values-sw320dp/dimens.xml +++ b/app/src/main/res/values-sw320dp/dimens.xml @@ -2,6 +2,11 @@ 17.7778sp 7.1111dp + 71.1111dp + 16.0000sp + 13.3333sp + 1.7778dp + 14.2222dp -53.3333dp -26.6667dp -17.7778dp @@ -11,7 +16,6 @@ -4.4444dp -1.7778dp -0.8889dp - 0.0000dp 0.0889dp 0.4444dp 0.8889dp diff --git a/app/src/main/res/values-sw384dp/dimens.xml b/app/src/main/res/values-sw384dp/dimens.xml index 2a944b1..42a87ea 100644 --- a/app/src/main/res/values-sw384dp/dimens.xml +++ b/app/src/main/res/values-sw384dp/dimens.xml @@ -2,6 +2,11 @@ 21.3333sp 8.5333dp + 85.3333dp + 19.2000sp + 16.0000sp + 2.1333dp + 17.0667dp -64.0000dp -32.0000dp -21.3333dp @@ -11,7 +16,6 @@ -5.3333dp -2.1333dp -1.0667dp - 0.0000dp 0.1067dp 0.5333dp 1.0667dp diff --git a/app/src/main/res/values-sw392dp/dimens.xml b/app/src/main/res/values-sw392dp/dimens.xml index 7d631c0..5ebc9fb 100644 --- a/app/src/main/res/values-sw392dp/dimens.xml +++ b/app/src/main/res/values-sw392dp/dimens.xml @@ -2,6 +2,11 @@ 21.7778sp 8.7111dp + 87.1111dp + 19.6000sp + 16.3333sp + 2.1778dp + 17.4222dp -65.3333dp -32.6667dp -21.7778dp @@ -11,7 +16,6 @@ -5.4444dp -2.1778dp -1.0889dp - 0.0000dp 0.1089dp 0.5444dp 1.0889dp diff --git a/app/src/main/res/values-sw400dp/dimens.xml b/app/src/main/res/values-sw400dp/dimens.xml index 1f76b7f..87ca748 100644 --- a/app/src/main/res/values-sw400dp/dimens.xml +++ b/app/src/main/res/values-sw400dp/dimens.xml @@ -2,6 +2,11 @@ 22.2222sp 8.8889dp + 88.8889dp + 20.0000sp + 16.6667sp + 2.2222dp + 17.7778dp -66.6667dp -33.3333dp -22.2222dp @@ -11,7 +16,6 @@ -5.5556dp -2.2222dp -1.1111dp - 0.0000dp 0.1111dp 0.5556dp 1.1111dp diff --git a/app/src/main/res/values-sw410dp/dimens.xml b/app/src/main/res/values-sw410dp/dimens.xml index b24557b..a2b9cec 100644 --- a/app/src/main/res/values-sw410dp/dimens.xml +++ b/app/src/main/res/values-sw410dp/dimens.xml @@ -2,6 +2,11 @@ 22.7778sp 9.1111dp + 91.1111dp + 20.5000sp + 17.0833sp + 2.2778dp + 18.2222dp -68.3333dp -34.1667dp -22.7778dp @@ -11,7 +16,6 @@ -5.6944dp -2.2778dp -1.1389dp - 0.0000dp 0.1139dp 0.5694dp 1.1389dp diff --git a/app/src/main/res/values-sw411dp/dimens.xml b/app/src/main/res/values-sw411dp/dimens.xml index 4f356d6..774f7ca 100644 --- a/app/src/main/res/values-sw411dp/dimens.xml +++ b/app/src/main/res/values-sw411dp/dimens.xml @@ -2,6 +2,11 @@ 22.8333sp 9.1333dp + 91.3333dp + 20.5500sp + 17.1250sp + 2.2833dp + 18.2667dp -68.5000dp -34.2500dp -22.8333dp @@ -11,7 +16,6 @@ -5.7083dp -2.2833dp -1.1417dp - 0.0000dp 0.1142dp 0.5708dp 1.1417dp diff --git a/app/src/main/res/values-sw432dp/dimens.xml b/app/src/main/res/values-sw432dp/dimens.xml index 90c013f..05693c0 100644 --- a/app/src/main/res/values-sw432dp/dimens.xml +++ b/app/src/main/res/values-sw432dp/dimens.xml @@ -2,6 +2,11 @@ 24.0000sp 9.6000dp + 96.0000dp + 21.6000sp + 18.0000sp + 2.4000dp + 19.2000dp -72.0000dp -36.0000dp -24.0000dp @@ -11,7 +16,6 @@ -6.0000dp -2.4000dp -1.2000dp - 0.0000dp 0.1200dp 0.6000dp 1.2000dp diff --git a/app/src/main/res/values-sw480dp/dimens.xml b/app/src/main/res/values-sw480dp/dimens.xml index eb1f60c..b5cc74d 100644 --- a/app/src/main/res/values-sw480dp/dimens.xml +++ b/app/src/main/res/values-sw480dp/dimens.xml @@ -2,6 +2,11 @@ 26.6667sp 10.6667dp + 106.6667dp + 24.0000sp + 20.0000sp + 2.6667dp + 21.3333dp -80.0000dp -40.0000dp -26.6667dp @@ -11,7 +16,6 @@ -6.6667dp -2.6667dp -1.3333dp - 0.0000dp 0.1333dp 0.6667dp 1.3333dp diff --git a/app/src/main/res/values-sw533dp/dimens.xml b/app/src/main/res/values-sw533dp/dimens.xml index a345e6a..ceccdd4 100644 --- a/app/src/main/res/values-sw533dp/dimens.xml +++ b/app/src/main/res/values-sw533dp/dimens.xml @@ -2,6 +2,11 @@ 29.6111sp 11.8444dp + 118.4444dp + 26.6500sp + 22.2083sp + 2.9611dp + 23.6889dp -88.8333dp -44.4167dp -29.6111dp @@ -11,7 +16,6 @@ -7.4028dp -2.9611dp -1.4806dp - 0.0000dp 0.1481dp 0.7403dp 1.4806dp diff --git a/app/src/main/res/values-sw592dp/dimens.xml b/app/src/main/res/values-sw592dp/dimens.xml index e970f5c..1317686 100644 --- a/app/src/main/res/values-sw592dp/dimens.xml +++ b/app/src/main/res/values-sw592dp/dimens.xml @@ -2,6 +2,11 @@ 32.8889sp 13.1556dp + 131.5556dp + 29.6000sp + 24.6667sp + 3.2889dp + 26.3111dp -98.6667dp -49.3333dp -32.8889dp @@ -11,7 +16,6 @@ -8.2222dp -3.2889dp -1.6444dp - 0.0000dp 0.1644dp 0.8222dp 1.6444dp diff --git a/app/src/main/res/values-sw600dp/dimens.xml b/app/src/main/res/values-sw600dp/dimens.xml index 1f5973c..13e56bf 100644 --- a/app/src/main/res/values-sw600dp/dimens.xml +++ b/app/src/main/res/values-sw600dp/dimens.xml @@ -2,6 +2,11 @@ 33.3333sp 13.3333dp + 133.3333dp + 30.0000sp + 25.0000sp + 3.3333dp + 26.6667dp -100.0000dp -50.0000dp -33.3333dp @@ -11,7 +16,6 @@ -8.3333dp -3.3333dp -1.6667dp - 0.0000dp 0.1667dp 0.8333dp 1.6667dp diff --git a/app/src/main/res/values-sw640dp/dimens.xml b/app/src/main/res/values-sw640dp/dimens.xml index 2e35328..8cb1d81 100644 --- a/app/src/main/res/values-sw640dp/dimens.xml +++ b/app/src/main/res/values-sw640dp/dimens.xml @@ -2,6 +2,11 @@ 35.5556sp 14.2222dp + 142.2222dp + 32.0000sp + 26.6667sp + 3.5556dp + 28.4444dp -106.6667dp -53.3333dp -35.5556dp @@ -11,7 +16,6 @@ -8.8889dp -3.5556dp -1.7778dp - 0.0000dp 0.1778dp 0.8889dp 1.7778dp diff --git a/app/src/main/res/values-sw662dp/dimens.xml b/app/src/main/res/values-sw662dp/dimens.xml index 849c7a2..2d4277e 100644 --- a/app/src/main/res/values-sw662dp/dimens.xml +++ b/app/src/main/res/values-sw662dp/dimens.xml @@ -2,6 +2,11 @@ 36.7778sp 14.7111dp + 147.1111dp + 33.1000sp + 27.5833sp + 3.6778dp + 29.4222dp -110.3333dp -55.1667dp -36.7778dp @@ -11,7 +16,6 @@ -9.1944dp -3.6778dp -1.8389dp - 0.0000dp 0.1839dp 0.9194dp 1.8389dp diff --git a/app/src/main/res/values-sw720dp/dimens.xml b/app/src/main/res/values-sw720dp/dimens.xml index b1cb1c8..ef1a1c4 100644 --- a/app/src/main/res/values-sw720dp/dimens.xml +++ b/app/src/main/res/values-sw720dp/dimens.xml @@ -2,6 +2,11 @@ 40.0000sp 16.0000dp + 160.0000dp + 36.0000sp + 30.0000sp + 4.0000dp + 32.0000dp -120.0000dp -60.0000dp -40.0000dp @@ -11,7 +16,6 @@ -10.0000dp -4.0000dp -2.0000dp - 0.0000dp 0.2000dp 1.0000dp 2.0000dp diff --git a/app/src/main/res/values-sw768dp/dimens.xml b/app/src/main/res/values-sw768dp/dimens.xml index 1133945..2df2464 100644 --- a/app/src/main/res/values-sw768dp/dimens.xml +++ b/app/src/main/res/values-sw768dp/dimens.xml @@ -2,6 +2,11 @@ 42.6667sp 17.0667dp + 170.6667dp + 38.4000sp + 32.0000sp + 4.2667dp + 34.1333dp -128.0000dp -64.0000dp -42.6667dp @@ -11,7 +16,6 @@ -10.6667dp -4.2667dp -2.1333dp - 0.0000dp 0.2133dp 1.0667dp 2.1333dp diff --git a/app/src/main/res/values-sw800dp/dimens.xml b/app/src/main/res/values-sw800dp/dimens.xml index fda319f..8bb1bd0 100644 --- a/app/src/main/res/values-sw800dp/dimens.xml +++ b/app/src/main/res/values-sw800dp/dimens.xml @@ -2,6 +2,11 @@ 44.4444sp 17.7778dp + 177.7778dp + 40.0000sp + 33.3333sp + 4.4444dp + 35.5556dp -133.3333dp -66.6667dp -44.4444dp @@ -11,7 +16,6 @@ -11.1111dp -4.4444dp -2.2222dp - 0.0000dp 0.2222dp 1.1111dp 2.2222dp diff --git a/app/src/main/res/values-sw811dp/dimens.xml b/app/src/main/res/values-sw811dp/dimens.xml index 36215b9..42ba871 100644 --- a/app/src/main/res/values-sw811dp/dimens.xml +++ b/app/src/main/res/values-sw811dp/dimens.xml @@ -2,6 +2,11 @@ 45.0556sp 18.0222dp + 180.2222dp + 40.5500sp + 33.7917sp + 4.5056dp + 36.0444dp -135.1667dp -67.5833dp -45.0556dp @@ -11,7 +16,6 @@ -11.2639dp -4.5056dp -2.2528dp - 0.0000dp 0.2253dp 1.1264dp 2.2528dp diff --git a/app/src/main/res/values-sw820dp/dimens.xml b/app/src/main/res/values-sw820dp/dimens.xml index f97c6df..83c0db5 100644 --- a/app/src/main/res/values-sw820dp/dimens.xml +++ b/app/src/main/res/values-sw820dp/dimens.xml @@ -2,6 +2,11 @@ 45.5556sp 18.2222dp + 182.2222dp + 41.0000sp + 34.1667sp + 4.5556dp + 36.4444dp -136.6667dp -68.3333dp -45.5556dp @@ -11,7 +16,6 @@ -11.3889dp -4.5556dp -2.2778dp - 0.0000dp 0.2278dp 1.1389dp 2.2778dp diff --git a/app/src/main/res/values-sw960dp/dimens.xml b/app/src/main/res/values-sw960dp/dimens.xml index f62d954..47f07b1 100644 --- a/app/src/main/res/values-sw960dp/dimens.xml +++ b/app/src/main/res/values-sw960dp/dimens.xml @@ -2,6 +2,11 @@ 53.3333sp 21.3333dp + 213.3333dp + 48.0000sp + 40.0000sp + 5.3333dp + 42.6667dp -160.0000dp -80.0000dp -53.3333dp @@ -11,7 +16,6 @@ -13.3333dp -5.3333dp -2.6667dp - 0.0000dp 0.2667dp 1.3333dp 2.6667dp diff --git a/app/src/main/res/values-sw961dp/dimens.xml b/app/src/main/res/values-sw961dp/dimens.xml index d366cd6..d4fef83 100644 --- a/app/src/main/res/values-sw961dp/dimens.xml +++ b/app/src/main/res/values-sw961dp/dimens.xml @@ -2,6 +2,11 @@ 53.3889sp 21.3556dp + 213.5556dp + 48.0500sp + 40.0417sp + 5.3389dp + 42.7111dp -160.1667dp -80.0833dp -53.3889dp @@ -11,7 +16,6 @@ -13.3472dp -5.3389dp -2.6694dp - 0.0000dp 0.2669dp 1.3347dp 2.6694dp diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index d98edcb..3c808ba 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -12,7 +12,7 @@ #000000 #000000 #9D9D9D - + #BFFFFFFF #00000000 @@ -254,4 +254,6 @@ #0000cd #00008b #000000 + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 94220af..5e43853 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -8,6 +8,8 @@ 15sp 2dp + 16dp + -60dp -30dp @@ -18,7 +20,6 @@ -5dp -2dp -1dp - 0dp 0.1dp 0.5dp 1dp diff --git a/build.gradle b/build.gradle index 416d867..d00dfcd 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { repositories { google() -// mavenCentral() + mavenCentral() maven { url 'https://jitpack.io' } maven { url 'https://maven.google.com' } maven { url 'https://developer.huawei.com/repo/' } @@ -35,7 +35,7 @@ allprojects { repositories { google() -// mavenCentral() + mavenCentral() maven { url 'https://jitpack.io' } maven { url 'https://maven.google.com' } maven { url 'https://developer.huawei.com/repo/' }